nuxt-ai-ready 3.3.1 → 3.5.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/chunks/agent-skills.mjs +2 -1
- package/dist/chunks/prerender.mjs +14 -2
- package/dist/cli.mjs +28 -10
- package/dist/module.d.mts +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -2
- package/dist/runtime/server/middleware/markdown.prerender.js +9 -1
- package/dist/runtime/server/plugins/content-signal-capture-nitro3.prerender.d.ts +2 -0
- package/dist/runtime/server/plugins/content-signal-capture-nitro3.prerender.js +13 -0
- package/dist/runtime/server/plugins/content-signal-capture.prerender.d.ts +2 -0
- package/dist/runtime/server/plugins/content-signal-capture.prerender.js +13 -0
- package/dist/runtime/server/routes/__ai-ready/cron.get.js +1 -1
- package/dist/runtime/server/routes/__ai-ready/poll.post.js +1 -1
- package/dist/runtime/server/routes/__ai-ready/prune.post.js +1 -1
- package/dist/runtime/server/routes/__ai-ready/reindex.post.js +1 -1
- package/dist/runtime/server/routes/__ai-ready/restore.post.js +1 -1
- package/dist/runtime/server/routes/__ai-ready/status.get.js +1 -1
- package/dist/runtime/server/utils/auth.d.ts +2 -4
- package/dist/runtime/server/utils/auth.js +26 -5
- package/dist/runtime/server/utils/cloudflare.d.ts +3 -0
- package/dist/runtime/server/utils/prerender-html.d.ts +3 -0
- package/dist/runtime/server/utils/prerender-html.js +12 -0
- package/dist/runtime/sync-secret.d.ts +4 -0
- package/dist/runtime/sync-secret.js +15 -0
- package/dist/runtime/types.d.ts +6 -5
- package/dist/shared/{nuxt-ai-ready.CKXM1jnE.mjs → nuxt-ai-ready.BaNK8yfw.mjs} +393 -266
- package/package.json +1 -1
- package/skills/nuxt-ai-ready/references/runtime-indexing.md +8 -2
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readFile, access, mkdir, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { readFile, access, mkdir, writeFile, rm } from 'node:fs/promises';
|
|
3
2
|
import { createRequire } from 'node:module';
|
|
4
3
|
import { dirname, basename, join, resolve, relative } from 'node:path';
|
|
5
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -11,7 +10,9 @@ import { r as readPackageMetadata, a as resolvePackageMetadataPath } from './nux
|
|
|
11
10
|
import { parseLlmsTxtConfig } from '../../dist/runtime/llms-txt-files.js';
|
|
12
11
|
import { toDeployedRoute } from '../../dist/runtime/route-path.js';
|
|
13
12
|
import { SITEMAP_MD_ROUTE } from '../../dist/runtime/server/utils/sitemap-md.js';
|
|
13
|
+
import { LEGACY_RUNTIME_SYNC_SECRET_WARNING, deriveRuntimeSyncSecret } from '../../dist/runtime/sync-secret.js';
|
|
14
14
|
import { publicCacheControl, mergeVaryHeader } from '../../dist/runtime/cache-control.js';
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
15
16
|
import { MCP_SERVER_CARD_MEDIA_TYPE, AI_CATALOG_MEDIA_TYPE } from '../../dist/runtime/server/utils/discovery-response.js';
|
|
16
17
|
import { parseTOML, parseJSONC } from 'confbox';
|
|
17
18
|
import { toMarkdownPath, hasMarkdownTwin, normalizePagePath } from '../../dist/runtime/markdown-path.js';
|
|
@@ -563,6 +564,334 @@ async function readCloudflareAssetRouting(rootDir) {
|
|
|
563
564
|
}
|
|
564
565
|
}
|
|
565
566
|
|
|
567
|
+
function escapeRegExp(value) {
|
|
568
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
569
|
+
}
|
|
570
|
+
function ensureStaticHeader(contents, route, name, value, mode = "default") {
|
|
571
|
+
const eol = contents.includes("\r\n") ? "\r\n" : "\n";
|
|
572
|
+
const routePattern = new RegExp(`^${escapeRegExp(route)}[\\t ]*\\r?$`, "gm");
|
|
573
|
+
const routeMatches = [...contents.matchAll(routePattern)];
|
|
574
|
+
const routeMatch = routeMatches.at(-1);
|
|
575
|
+
if (!routeMatch || routeMatch.index === void 0) {
|
|
576
|
+
const separator = contents.length === 0 ? "" : contents.endsWith(`${eol}${eol}`) ? "" : contents.endsWith(eol) ? eol : `${eol}${eol}`;
|
|
577
|
+
return `${contents}${separator}${route}${eol} ${name}: ${value}${eol}`;
|
|
578
|
+
}
|
|
579
|
+
const routeLineEnd = routeMatch.index + routeMatch[0].length;
|
|
580
|
+
const blockStart = contents[routeLineEnd] === "\n" ? routeLineEnd + 1 : routeLineEnd;
|
|
581
|
+
const nextBlockPattern = /^(?![\t ]|\r?$).+/gm;
|
|
582
|
+
nextBlockPattern.lastIndex = blockStart;
|
|
583
|
+
const nextBlock = nextBlockPattern.exec(contents);
|
|
584
|
+
const blockEnd = nextBlock?.index ?? contents.length;
|
|
585
|
+
const block = contents.slice(blockStart, blockEnd);
|
|
586
|
+
const escapedName = escapeRegExp(name);
|
|
587
|
+
const existingHeaderPattern = new RegExp(
|
|
588
|
+
`^[\\t ]+(?:${escapedName}[\\t ]*:|![\\t ]*${escapedName}[\\t ]*\\r?$)`,
|
|
589
|
+
"im"
|
|
590
|
+
);
|
|
591
|
+
if (existingHeaderPattern.test(block)) {
|
|
592
|
+
if (mode === "append") {
|
|
593
|
+
const removalPattern = new RegExp(`^[\\t ]+![\\t ]*${escapedName}[\\t ]*\\r?$`, "im");
|
|
594
|
+
if (removalPattern.test(block))
|
|
595
|
+
return contents;
|
|
596
|
+
const valuePattern = new RegExp(`^([\\t ]+${escapedName}[\\t ]*:[\\t ]*)([^\\r\\n]*)(\\r?$)`, "im");
|
|
597
|
+
const match = block.match(valuePattern);
|
|
598
|
+
if (match) {
|
|
599
|
+
const merged = block.replace(valuePattern, (_line, prefix2, current, suffix) => {
|
|
600
|
+
const appended = name.toLowerCase() === "link" ? current.includes(value) ? current : [current, value].filter(Boolean).join(", ") : mergeVaryHeader(current, value);
|
|
601
|
+
return `${prefix2}${appended}${suffix}`;
|
|
602
|
+
});
|
|
603
|
+
return `${contents.slice(0, blockStart)}${merged}${contents.slice(blockEnd)}`;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return contents;
|
|
607
|
+
}
|
|
608
|
+
const prefix = blockStart === routeLineEnd ? eol : "";
|
|
609
|
+
return `${contents.slice(0, blockStart)}${prefix} ${name}: ${value}${eol}${contents.slice(blockStart)}`;
|
|
610
|
+
}
|
|
611
|
+
function mergeStaticDiscoveryHeaders(contents, baseURL, entries) {
|
|
612
|
+
if (entries.length === 0)
|
|
613
|
+
return contents;
|
|
614
|
+
const route = toDeployedRoute("/*", baseURL);
|
|
615
|
+
const blocks = splitHeaderBlocks(contents);
|
|
616
|
+
const matching = blocks.filter((block) => block.route === route);
|
|
617
|
+
let merged = matching[0]?.text || "";
|
|
618
|
+
for (const block of matching.slice(1)) {
|
|
619
|
+
for (const line of block.text.split(/(?<=\n)/).slice(1)) {
|
|
620
|
+
const header = line.match(/^[\t ]+([^:\s]+):[\t ]*([^\r\n]*)/);
|
|
621
|
+
if (header?.[1]?.toLowerCase() === "link") {
|
|
622
|
+
merged = ensureStaticHeader(merged, route, header[1], header[2], "append");
|
|
623
|
+
} else if (header) {
|
|
624
|
+
const existing = new RegExp(`^[\\t ]+${escapeRegExp(header[1])}[\\t ]*:[^\\r\\n]*(?:\\r?\\n|$)`, "gim");
|
|
625
|
+
merged = ensureStaticHeader(merged.replace(existing, ""), route, header[1], header[2]);
|
|
626
|
+
} else {
|
|
627
|
+
merged += line;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (matching.length > 1) {
|
|
632
|
+
let inserted = false;
|
|
633
|
+
contents = blocks.map((block) => {
|
|
634
|
+
if (block.route !== route)
|
|
635
|
+
return block.text;
|
|
636
|
+
if (inserted)
|
|
637
|
+
return "";
|
|
638
|
+
inserted = true;
|
|
639
|
+
return merged;
|
|
640
|
+
}).join("");
|
|
641
|
+
}
|
|
642
|
+
return entries.reduce((headers, entry) => ensureStaticHeader(headers, route, "Link", entry, "append"), contents);
|
|
643
|
+
}
|
|
644
|
+
function parseHeaderLine(line) {
|
|
645
|
+
const colon = line.indexOf(":");
|
|
646
|
+
if (!/^[\t ]/.test(line) || colon < 0)
|
|
647
|
+
return void 0;
|
|
648
|
+
const name = line.slice(0, colon).trim();
|
|
649
|
+
if (!name || name.startsWith("!") || /\s/.test(name))
|
|
650
|
+
return void 0;
|
|
651
|
+
const eol = line.endsWith("\r\n") ? "\r\n" : line.endsWith("\n") ? "\n" : "";
|
|
652
|
+
return {
|
|
653
|
+
name,
|
|
654
|
+
value: line.slice(colon + 1, line.length - eol.length).trim(),
|
|
655
|
+
withValue: (value) => `${line.slice(0, colon + 1)} ${value}${eol}`
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
const RE_DETACH_LINE = /^[\t ]+![\t ]*([^\s:]+)/;
|
|
659
|
+
function patternToRegExp(pattern) {
|
|
660
|
+
const source = pattern.split(/(\*|:\w+)/).map((part) => part === "*" ? ".*" : part.startsWith(":") ? "[^/]+" : escapeRegExp(part)).join("");
|
|
661
|
+
return new RegExp(`^${source}$`);
|
|
662
|
+
}
|
|
663
|
+
function matchingStaticContentSignals(routeRules, route) {
|
|
664
|
+
return Object.entries(routeRules).flatMap(([pattern, rule]) => {
|
|
665
|
+
if (!patternToRegExp(pattern).test(route))
|
|
666
|
+
return [];
|
|
667
|
+
const headers = rule?.headers;
|
|
668
|
+
return Object.entries(headers || {}).filter(([name]) => name.toLowerCase() === "content-signal").map(([, value]) => value);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
function normalizeStaticContentSignalHeaders(contents) {
|
|
672
|
+
const parsed = splitHeaderBlocks(contents);
|
|
673
|
+
if (!parsed.some((block) => /^\s+content-signal\s*:/im.test(block.text)))
|
|
674
|
+
return contents;
|
|
675
|
+
const policyLine = /^[\t ]+(?:content-signal\s*:.*|!\s*content-signal\s*)$/i;
|
|
676
|
+
const hasPolicy = (block) => block.text.split(/\r?\n/).slice(1).some((line) => policyLine.test(line));
|
|
677
|
+
const repeated = /* @__PURE__ */ new Map();
|
|
678
|
+
for (const block of parsed) {
|
|
679
|
+
if (block.route !== null)
|
|
680
|
+
repeated.set(block.route, [...repeated.get(block.route) || [], block]);
|
|
681
|
+
}
|
|
682
|
+
const combined = /* @__PURE__ */ new Set();
|
|
683
|
+
const blocks = parsed.flatMap((block) => {
|
|
684
|
+
const group = block.route === null ? [] : repeated.get(block.route);
|
|
685
|
+
if (group.length < 2 || !group.some(hasPolicy))
|
|
686
|
+
return [block];
|
|
687
|
+
if (combined.has(block.route))
|
|
688
|
+
return [];
|
|
689
|
+
combined.add(block.route);
|
|
690
|
+
const lines = group.at(-1).text.split(/\r?\n/).slice(1).filter((line) => !policyLine.test(line));
|
|
691
|
+
const policy = group.filter(hasPolicy).at(-1).text.split(/\r?\n/).slice(1).filter((line) => policyLine.test(line));
|
|
692
|
+
return [{ route: block.route, text: `${block.route}
|
|
693
|
+
${[...lines, ...policy].join("\n")}
|
|
694
|
+
` }];
|
|
695
|
+
});
|
|
696
|
+
const policyOnly = (block) => hasPolicy(block) && block.text.split(/\r?\n/).slice(1).every((line) => !line.trim() || line.trimStart().startsWith("#") || policyLine.test(line));
|
|
697
|
+
const policyValue = (block) => block.text.match(/^[\t ]+content-signal[\t ]*:(.*)$/im)?.[1]?.trim() ?? null;
|
|
698
|
+
const edges = blocks.map(() => /* @__PURE__ */ new Set());
|
|
699
|
+
const incoming = blocks.map(() => 0);
|
|
700
|
+
const connect = (before, after) => {
|
|
701
|
+
if (!edges[before].has(after)) {
|
|
702
|
+
edges[before].add(after);
|
|
703
|
+
incoming[after]++;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
let previousUnrelated;
|
|
707
|
+
for (let index = 0; index < blocks.length; index++) {
|
|
708
|
+
if (!policyOnly(blocks[index])) {
|
|
709
|
+
if (previousUnrelated !== void 0)
|
|
710
|
+
connect(previousUnrelated, index);
|
|
711
|
+
previousUnrelated = index;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
for (let broad = 0; broad < blocks.length; broad++) {
|
|
715
|
+
const source = blocks[broad];
|
|
716
|
+
if (!hasPolicy(source) || source.route === null)
|
|
717
|
+
continue;
|
|
718
|
+
for (let narrow = 0; narrow < blocks.length; narrow++) {
|
|
719
|
+
const target = blocks[narrow];
|
|
720
|
+
if (broad !== narrow && hasPolicy(target) && target.route !== null && policyValue(source) !== policyValue(target) && coversContentSignalPattern(source.route, target.route)) {
|
|
721
|
+
connect(broad, narrow);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const ordered = [];
|
|
726
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
727
|
+
while (ordered.length < blocks.length) {
|
|
728
|
+
const next = incoming.findIndex((count, index) => count === 0 && !emitted.has(index));
|
|
729
|
+
if (next === -1)
|
|
730
|
+
throw new Error("Content-Signal rules conflict with other header ordering. Place broader mixed policy rules before narrower rules.");
|
|
731
|
+
emitted.add(next);
|
|
732
|
+
ordered.push(blocks[next]);
|
|
733
|
+
for (const target of edges[next])
|
|
734
|
+
incoming[target]--;
|
|
735
|
+
}
|
|
736
|
+
return ordered.map((block) => {
|
|
737
|
+
const text = block.text.trimEnd();
|
|
738
|
+
if (!/^\s+content-signal\s*:/im.test(text) || /^\s+!\s*content-signal\s*$/im.test(text))
|
|
739
|
+
return `${text}
|
|
740
|
+
`;
|
|
741
|
+
return `${text.replace(/^(\s+)(content-signal\s*:)/im, "$1! Content-Signal\n$1$2")}
|
|
742
|
+
`;
|
|
743
|
+
}).join("\n");
|
|
744
|
+
}
|
|
745
|
+
function coversPattern(broad, narrow) {
|
|
746
|
+
if (broad === narrow || !broad.startsWith("/") || !narrow.startsWith("/"))
|
|
747
|
+
return false;
|
|
748
|
+
if (!/[*:]/.test(narrow))
|
|
749
|
+
return patternToRegExp(broad).test(narrow);
|
|
750
|
+
return broad.indexOf("*") === broad.length - 1 && !broad.includes(":") && narrow.startsWith(broad.slice(0, -1));
|
|
751
|
+
}
|
|
752
|
+
function coversContentSignalPattern(broad, narrow) {
|
|
753
|
+
if (coversPattern(broad, narrow))
|
|
754
|
+
return true;
|
|
755
|
+
if (broad === narrow || !broad.startsWith("/") || !narrow.startsWith("/") || broad.indexOf("*") !== broad.length - 1 || !broad.includes(":")) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
const prefix = narrow.split("*")[0];
|
|
759
|
+
return !prefix.includes(":") && patternToRegExp(broad).test(prefix);
|
|
760
|
+
}
|
|
761
|
+
function splitLinkEntries(value) {
|
|
762
|
+
const entries = [];
|
|
763
|
+
let current = "";
|
|
764
|
+
let inUrl = false;
|
|
765
|
+
let inQuote = false;
|
|
766
|
+
for (const char of value) {
|
|
767
|
+
if (char === "<" && !inQuote)
|
|
768
|
+
inUrl = true;
|
|
769
|
+
else if (char === ">" && !inQuote)
|
|
770
|
+
inUrl = false;
|
|
771
|
+
else if (char === '"' && !inUrl)
|
|
772
|
+
inQuote = !inQuote;
|
|
773
|
+
if (char === "," && !inUrl && !inQuote) {
|
|
774
|
+
entries.push(current.trim());
|
|
775
|
+
current = "";
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
current += char;
|
|
779
|
+
}
|
|
780
|
+
entries.push(current.trim());
|
|
781
|
+
return entries.filter(Boolean);
|
|
782
|
+
}
|
|
783
|
+
function headerTokens(name, value) {
|
|
784
|
+
if (name === "link")
|
|
785
|
+
return splitLinkEntries(value);
|
|
786
|
+
if (name === "vary")
|
|
787
|
+
return value.split(",").map((token) => token.trim()).filter(Boolean);
|
|
788
|
+
return [value.trim()];
|
|
789
|
+
}
|
|
790
|
+
function tokenKey(name, token) {
|
|
791
|
+
return name === "vary" ? token.toLowerCase() : token;
|
|
792
|
+
}
|
|
793
|
+
function dedupeStaticHeaders(contents, names) {
|
|
794
|
+
const owned = new Set(names.map((name) => name.toLowerCase()));
|
|
795
|
+
const blocks = splitHeaderBlocks(contents);
|
|
796
|
+
const detached = /* @__PURE__ */ new Set();
|
|
797
|
+
const parsed = blocks.map((block) => {
|
|
798
|
+
const headers = [];
|
|
799
|
+
for (const line of block.text.split(/(?<=\n)/).slice(block.route === null ? 0 : 1)) {
|
|
800
|
+
const detach = line.match(RE_DETACH_LINE);
|
|
801
|
+
if (detach) {
|
|
802
|
+
detached.add(detach[1].toLowerCase());
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const header = parseHeaderLine(line);
|
|
806
|
+
if (!header)
|
|
807
|
+
continue;
|
|
808
|
+
const name = header.name.toLowerCase();
|
|
809
|
+
if (owned.has(name))
|
|
810
|
+
headers.push({ name, tokens: headerTokens(name, header.value) });
|
|
811
|
+
}
|
|
812
|
+
return headers;
|
|
813
|
+
});
|
|
814
|
+
const lastRuleIndex = /* @__PURE__ */ new Map();
|
|
815
|
+
blocks.forEach((block, index) => {
|
|
816
|
+
if (block.route !== null)
|
|
817
|
+
lastRuleIndex.set(block.route, index);
|
|
818
|
+
});
|
|
819
|
+
let changed = false;
|
|
820
|
+
const output = blocks.map((block) => {
|
|
821
|
+
const route = block.route;
|
|
822
|
+
if (route === null)
|
|
823
|
+
return block.text;
|
|
824
|
+
const provided = /* @__PURE__ */ new Map();
|
|
825
|
+
lastRuleIndex.forEach((index, pattern) => {
|
|
826
|
+
if (!coversPattern(pattern, route))
|
|
827
|
+
return;
|
|
828
|
+
for (const { name, tokens } of parsed[index]) {
|
|
829
|
+
if (detached.has(name))
|
|
830
|
+
continue;
|
|
831
|
+
const values = provided.get(name) ?? /* @__PURE__ */ new Set();
|
|
832
|
+
tokens.forEach((token) => values.add(tokenKey(name, token)));
|
|
833
|
+
provided.set(name, values);
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
if (provided.size === 0)
|
|
837
|
+
return block.text;
|
|
838
|
+
const [routeLine, ...lines] = block.text.split(/(?<=\n)/);
|
|
839
|
+
let headerLines = 0;
|
|
840
|
+
const kept = lines.flatMap((line) => {
|
|
841
|
+
const header = parseHeaderLine(line);
|
|
842
|
+
if (!header) {
|
|
843
|
+
if (RE_DETACH_LINE.test(line))
|
|
844
|
+
headerLines++;
|
|
845
|
+
return [line];
|
|
846
|
+
}
|
|
847
|
+
headerLines++;
|
|
848
|
+
const name = header.name.toLowerCase();
|
|
849
|
+
const covered = provided.get(name);
|
|
850
|
+
if (!covered)
|
|
851
|
+
return [line];
|
|
852
|
+
const tokens = headerTokens(name, header.value);
|
|
853
|
+
const remaining = tokens.filter((token) => !covered.has(tokenKey(name, token)));
|
|
854
|
+
if (remaining.length === tokens.length)
|
|
855
|
+
return [line];
|
|
856
|
+
changed = true;
|
|
857
|
+
if (remaining.length > 0)
|
|
858
|
+
return [header.withValue(remaining.join(", "))];
|
|
859
|
+
headerLines--;
|
|
860
|
+
return [];
|
|
861
|
+
});
|
|
862
|
+
return headerLines === 0 ? kept.join("") : `${routeLine}${kept.join("")}`;
|
|
863
|
+
});
|
|
864
|
+
return changed ? output.join("") : contents;
|
|
865
|
+
}
|
|
866
|
+
const CLOUDFLARE_STATIC_HEADER_RULE_LIMIT = 100;
|
|
867
|
+
const RE_EXACT_MARKDOWN_ROUTE$1 = /^\/[^*\s]*\.md[\t ]*\r?$/;
|
|
868
|
+
function splitHeaderBlocks(contents) {
|
|
869
|
+
const blocks = [];
|
|
870
|
+
let current = { route: null, text: "" };
|
|
871
|
+
for (const line of contents.split(/(?<=\n)/)) {
|
|
872
|
+
const isRoute = /^[^\s#]/.test(line);
|
|
873
|
+
if (isRoute) {
|
|
874
|
+
blocks.push(current);
|
|
875
|
+
current = { route: line.trimEnd(), text: "" };
|
|
876
|
+
}
|
|
877
|
+
current.text += line;
|
|
878
|
+
}
|
|
879
|
+
blocks.push(current);
|
|
880
|
+
return blocks;
|
|
881
|
+
}
|
|
882
|
+
function enforceStaticHeaderBudget(contents, limit) {
|
|
883
|
+
const blocks = splitHeaderBlocks(contents);
|
|
884
|
+
const ruleCount = blocks.filter((block) => block.route !== null).length;
|
|
885
|
+
if (ruleCount <= limit)
|
|
886
|
+
return { contents, total: ruleCount, dropped: 0 };
|
|
887
|
+
const kept = blocks.filter((block) => block.route === null || !RE_EXACT_MARKDOWN_ROUTE$1.test(block.route) || /^\s+(?:content-signal\s*:|!\s*content-signal\s*$)/im.test(block.text));
|
|
888
|
+
return {
|
|
889
|
+
contents: kept.map((block) => block.text).join(""),
|
|
890
|
+
total: kept.filter((block) => block.route !== null).length,
|
|
891
|
+
dropped: ruleCount - kept.filter((block) => block.route !== null).length
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
566
895
|
const RE_MD_EXT = /\.md$/;
|
|
567
896
|
function isStaticMarkdownSourceRoute(route) {
|
|
568
897
|
if (route.includes("*") || route.includes(":"))
|
|
@@ -607,41 +936,68 @@ function prerenderedMarkdownHeaderRules(prerenderedRoutes, baseURL, describedby,
|
|
|
607
936
|
headers: {
|
|
608
937
|
"Content-Type": "text/markdown; charset=utf-8",
|
|
609
938
|
"Vary": "Accept",
|
|
610
|
-
"Link": buildStaticMarkdownLinkHeader(pageRoute, baseURL, describedby)
|
|
939
|
+
"Link": buildStaticMarkdownLinkHeader(pageRoute, baseURL, describedby),
|
|
940
|
+
...entry.contentSignal !== void 0 && { "Content-Signal": entry.contentSignal }
|
|
611
941
|
}
|
|
612
942
|
});
|
|
613
943
|
}
|
|
614
944
|
}
|
|
615
945
|
return [...rules.values()];
|
|
616
946
|
}
|
|
617
|
-
const RE_EXACT_MARKDOWN_ROUTE
|
|
947
|
+
const RE_EXACT_MARKDOWN_ROUTE = /^\/[^*:\s]*\.md$/;
|
|
618
948
|
function isStaticHeaderRouteRule([, rule]) {
|
|
619
949
|
return Boolean(rule && "headers" in rule && rule.headers);
|
|
620
950
|
}
|
|
951
|
+
function hasContentSignal$1(headers) {
|
|
952
|
+
return Object.keys(headers || {}).some((name) => name.toLowerCase() === "content-signal");
|
|
953
|
+
}
|
|
621
954
|
function planStaticMarkdownHeaderRules(routeRules, rules, limit) {
|
|
955
|
+
rules = rules.map((rule) => {
|
|
956
|
+
if (!hasContentSignal$1(rule.headers) || matchingStaticContentSignals(routeRules, rule.route).length === 0)
|
|
957
|
+
return rule;
|
|
958
|
+
return { ...rule, headers: Object.fromEntries(Object.entries(rule.headers).filter(([name]) => name.toLowerCase() !== "content-signal")) };
|
|
959
|
+
});
|
|
622
960
|
if (limit === null)
|
|
623
961
|
return { _tag: "apply", rules };
|
|
624
962
|
const entries = Object.entries(routeRules).filter(isStaticHeaderRouteRule);
|
|
625
|
-
const registeredMarkdown = entries.map(([route]) => route).filter((route) => RE_EXACT_MARKDOWN_ROUTE
|
|
963
|
+
const registeredMarkdown = entries.map(([route]) => route).filter((route) => RE_EXACT_MARKDOWN_ROUTE.test(route));
|
|
626
964
|
const other = entries.length - registeredMarkdown.length;
|
|
627
965
|
const markdown = /* @__PURE__ */ new Set([...registeredMarkdown, ...rules.map((rule) => rule.route)]);
|
|
628
966
|
const total = other + markdown.size;
|
|
629
967
|
if (total <= limit)
|
|
630
968
|
return { _tag: "apply", rules };
|
|
631
|
-
|
|
969
|
+
const policyRules = rules.filter((rule) => hasContentSignal$1(rule.headers));
|
|
970
|
+
const policyRoutes = /* @__PURE__ */ new Set([
|
|
971
|
+
...policyRules.map((rule) => rule.route),
|
|
972
|
+
...registeredMarkdown.filter((route) => hasContentSignal$1(routeRules[route].headers))
|
|
973
|
+
]);
|
|
974
|
+
if (other + policyRoutes.size > limit)
|
|
975
|
+
return { _tag: "blocked", total: other + policyRoutes.size, limit };
|
|
976
|
+
return { _tag: "skip", drop: registeredMarkdown.filter((route) => !policyRoutes.has(route)), total, dropped: markdown.size - policyRoutes.size, rules: policyRules };
|
|
632
977
|
}
|
|
633
978
|
function applyStaticMarkdownHeaderPlan(routeRules, plan) {
|
|
979
|
+
if (plan._tag === "blocked")
|
|
980
|
+
throw new Error(`Content-Signal policy rules exceed the static header limit (${plan.total}/${plan.limit}). Reduce per-page policies or use a shared policy.`);
|
|
634
981
|
if (plan._tag === "skip") {
|
|
635
982
|
for (const route of plan.drop) {
|
|
636
983
|
const rule = routeRules[route];
|
|
637
984
|
if (rule)
|
|
638
985
|
rule.headers = void 0;
|
|
639
986
|
}
|
|
640
|
-
return;
|
|
641
987
|
}
|
|
642
988
|
for (const { route, headers } of plan.rules) {
|
|
643
989
|
const existing = routeRules[route];
|
|
644
990
|
const merged = defu({ headers: { ...headers } }, existing);
|
|
991
|
+
if (hasContentSignal$1(existing?.headers)) {
|
|
992
|
+
for (const name of Object.keys(merged.headers)) {
|
|
993
|
+
if (name.toLowerCase() === "content-signal")
|
|
994
|
+
delete merged.headers[name];
|
|
995
|
+
}
|
|
996
|
+
for (const [name, value] of Object.entries(existing.headers)) {
|
|
997
|
+
if (name.toLowerCase() === "content-signal")
|
|
998
|
+
merged.headers[name] = value;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
645
1001
|
const vary = Object.entries(existing?.headers || {}).filter(([name]) => name.toLowerCase() === "vary").map(([, value]) => value).join(", ");
|
|
646
1002
|
if (headers.Vary) {
|
|
647
1003
|
for (const name of Object.keys(merged.headers)) {
|
|
@@ -788,7 +1144,8 @@ function resolveContentSignalPolicy(option) {
|
|
|
788
1144
|
_tag: "Configured",
|
|
789
1145
|
// One comma-separated line: validators reject one line per signal.
|
|
790
1146
|
contentSignal: `ai-train=${value(option.aiTrain)}, search=${value(option.search)}, ai-input=${value(option.aiInput)}`,
|
|
791
|
-
|
|
1147
|
+
// Opt-in: Lighthouse's robots-txt audit fails Content-Usage as an unknown directive.
|
|
1148
|
+
...option.contentUsage === true && { contentUsage: `train-ai=${option.aiTrain ? "y" : "n"}` }
|
|
792
1149
|
};
|
|
793
1150
|
}
|
|
794
1151
|
function hasContentSignal(value) {
|
|
@@ -829,6 +1186,8 @@ function applyContentSignalPolicy(groups, policy) {
|
|
|
829
1186
|
}
|
|
830
1187
|
}
|
|
831
1188
|
|
|
1189
|
+
const AI_READY_CRON_SCHEDULE = "*/5 * * * *";
|
|
1190
|
+
|
|
832
1191
|
const DATABASE_TYPE_NONE = "none";
|
|
833
1192
|
const DATABASE_DEPENDENT_OPTIONS = ["runtimeSync", "cron"];
|
|
834
1193
|
const DATABASE_CONSUMER_LABELS = {
|
|
@@ -1256,243 +1615,6 @@ function opaqueJsonModule(exports) {
|
|
|
1256
1615
|
].join("\n");
|
|
1257
1616
|
}
|
|
1258
1617
|
|
|
1259
|
-
function escapeRegExp(value) {
|
|
1260
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1261
|
-
}
|
|
1262
|
-
function ensureStaticHeader(contents, route, name, value, mode = "default") {
|
|
1263
|
-
const eol = contents.includes("\r\n") ? "\r\n" : "\n";
|
|
1264
|
-
const routePattern = new RegExp(`^${escapeRegExp(route)}[\\t ]*\\r?$`, "gm");
|
|
1265
|
-
const routeMatches = [...contents.matchAll(routePattern)];
|
|
1266
|
-
const routeMatch = routeMatches.at(-1);
|
|
1267
|
-
if (!routeMatch || routeMatch.index === void 0) {
|
|
1268
|
-
const separator = contents.length === 0 ? "" : contents.endsWith(`${eol}${eol}`) ? "" : contents.endsWith(eol) ? eol : `${eol}${eol}`;
|
|
1269
|
-
return `${contents}${separator}${route}${eol} ${name}: ${value}${eol}`;
|
|
1270
|
-
}
|
|
1271
|
-
const routeLineEnd = routeMatch.index + routeMatch[0].length;
|
|
1272
|
-
const blockStart = contents[routeLineEnd] === "\n" ? routeLineEnd + 1 : routeLineEnd;
|
|
1273
|
-
const nextBlockPattern = /^(?![\t ]|\r?$).+/gm;
|
|
1274
|
-
nextBlockPattern.lastIndex = blockStart;
|
|
1275
|
-
const nextBlock = nextBlockPattern.exec(contents);
|
|
1276
|
-
const blockEnd = nextBlock?.index ?? contents.length;
|
|
1277
|
-
const block = contents.slice(blockStart, blockEnd);
|
|
1278
|
-
const escapedName = escapeRegExp(name);
|
|
1279
|
-
const existingHeaderPattern = new RegExp(
|
|
1280
|
-
`^[\\t ]+(?:${escapedName}[\\t ]*:|![\\t ]*${escapedName}[\\t ]*\\r?$)`,
|
|
1281
|
-
"im"
|
|
1282
|
-
);
|
|
1283
|
-
if (existingHeaderPattern.test(block)) {
|
|
1284
|
-
if (mode === "append") {
|
|
1285
|
-
const removalPattern = new RegExp(`^[\\t ]+![\\t ]*${escapedName}[\\t ]*\\r?$`, "im");
|
|
1286
|
-
if (removalPattern.test(block))
|
|
1287
|
-
return contents;
|
|
1288
|
-
const valuePattern = new RegExp(`^([\\t ]+${escapedName}[\\t ]*:[\\t ]*)([^\\r\\n]*)(\\r?$)`, "im");
|
|
1289
|
-
const match = block.match(valuePattern);
|
|
1290
|
-
if (match) {
|
|
1291
|
-
const merged = block.replace(valuePattern, (_line, prefix2, current, suffix) => {
|
|
1292
|
-
const appended = name.toLowerCase() === "link" ? current.includes(value) ? current : [current, value].filter(Boolean).join(", ") : mergeVaryHeader(current, value);
|
|
1293
|
-
return `${prefix2}${appended}${suffix}`;
|
|
1294
|
-
});
|
|
1295
|
-
return `${contents.slice(0, blockStart)}${merged}${contents.slice(blockEnd)}`;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
return contents;
|
|
1299
|
-
}
|
|
1300
|
-
const prefix = blockStart === routeLineEnd ? eol : "";
|
|
1301
|
-
return `${contents.slice(0, blockStart)}${prefix} ${name}: ${value}${eol}${contents.slice(blockStart)}`;
|
|
1302
|
-
}
|
|
1303
|
-
function mergeStaticDiscoveryHeaders(contents, baseURL, entries) {
|
|
1304
|
-
if (entries.length === 0)
|
|
1305
|
-
return contents;
|
|
1306
|
-
const route = toDeployedRoute("/*", baseURL);
|
|
1307
|
-
const blocks = splitHeaderBlocks(contents);
|
|
1308
|
-
const matching = blocks.filter((block) => block.route === route);
|
|
1309
|
-
let merged = matching[0]?.text || "";
|
|
1310
|
-
for (const block of matching.slice(1)) {
|
|
1311
|
-
for (const line of block.text.split(/(?<=\n)/).slice(1)) {
|
|
1312
|
-
const header = line.match(/^[\t ]+([^:\s]+):[\t ]*([^\r\n]*)/);
|
|
1313
|
-
if (header?.[1]?.toLowerCase() === "link") {
|
|
1314
|
-
merged = ensureStaticHeader(merged, route, header[1], header[2], "append");
|
|
1315
|
-
} else if (header) {
|
|
1316
|
-
const existing = new RegExp(`^[\\t ]+${escapeRegExp(header[1])}[\\t ]*:[^\\r\\n]*(?:\\r?\\n|$)`, "gim");
|
|
1317
|
-
merged = ensureStaticHeader(merged.replace(existing, ""), route, header[1], header[2]);
|
|
1318
|
-
} else {
|
|
1319
|
-
merged += line;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
if (matching.length > 1) {
|
|
1324
|
-
let inserted = false;
|
|
1325
|
-
contents = blocks.map((block) => {
|
|
1326
|
-
if (block.route !== route)
|
|
1327
|
-
return block.text;
|
|
1328
|
-
if (inserted)
|
|
1329
|
-
return "";
|
|
1330
|
-
inserted = true;
|
|
1331
|
-
return merged;
|
|
1332
|
-
}).join("");
|
|
1333
|
-
}
|
|
1334
|
-
return entries.reduce((headers, entry) => ensureStaticHeader(headers, route, "Link", entry, "append"), contents);
|
|
1335
|
-
}
|
|
1336
|
-
function parseHeaderLine(line) {
|
|
1337
|
-
const colon = line.indexOf(":");
|
|
1338
|
-
if (!/^[\t ]/.test(line) || colon < 0)
|
|
1339
|
-
return void 0;
|
|
1340
|
-
const name = line.slice(0, colon).trim();
|
|
1341
|
-
if (!name || name.startsWith("!") || /\s/.test(name))
|
|
1342
|
-
return void 0;
|
|
1343
|
-
const eol = line.endsWith("\r\n") ? "\r\n" : line.endsWith("\n") ? "\n" : "";
|
|
1344
|
-
return {
|
|
1345
|
-
name,
|
|
1346
|
-
value: line.slice(colon + 1, line.length - eol.length).trim(),
|
|
1347
|
-
withValue: (value) => `${line.slice(0, colon + 1)} ${value}${eol}`
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
const RE_DETACH_LINE = /^[\t ]+![\t ]*([^\s:]+)/;
|
|
1351
|
-
function patternToRegExp(pattern) {
|
|
1352
|
-
const source = pattern.split(/(\*|:\w+)/).map((part) => part === "*" ? ".*" : part.startsWith(":") ? "[^/]+" : escapeRegExp(part)).join("");
|
|
1353
|
-
return new RegExp(`^${source}$`);
|
|
1354
|
-
}
|
|
1355
|
-
function coversPattern(broad, narrow) {
|
|
1356
|
-
if (broad === narrow || !broad.startsWith("/") || !narrow.startsWith("/"))
|
|
1357
|
-
return false;
|
|
1358
|
-
if (!/[*:]/.test(narrow))
|
|
1359
|
-
return patternToRegExp(broad).test(narrow);
|
|
1360
|
-
return broad.indexOf("*") === broad.length - 1 && !broad.includes(":") && narrow.startsWith(broad.slice(0, -1));
|
|
1361
|
-
}
|
|
1362
|
-
function splitLinkEntries(value) {
|
|
1363
|
-
const entries = [];
|
|
1364
|
-
let current = "";
|
|
1365
|
-
let inUrl = false;
|
|
1366
|
-
let inQuote = false;
|
|
1367
|
-
for (const char of value) {
|
|
1368
|
-
if (char === "<" && !inQuote)
|
|
1369
|
-
inUrl = true;
|
|
1370
|
-
else if (char === ">" && !inQuote)
|
|
1371
|
-
inUrl = false;
|
|
1372
|
-
else if (char === '"' && !inUrl)
|
|
1373
|
-
inQuote = !inQuote;
|
|
1374
|
-
if (char === "," && !inUrl && !inQuote) {
|
|
1375
|
-
entries.push(current.trim());
|
|
1376
|
-
current = "";
|
|
1377
|
-
continue;
|
|
1378
|
-
}
|
|
1379
|
-
current += char;
|
|
1380
|
-
}
|
|
1381
|
-
entries.push(current.trim());
|
|
1382
|
-
return entries.filter(Boolean);
|
|
1383
|
-
}
|
|
1384
|
-
function headerTokens(name, value) {
|
|
1385
|
-
if (name === "link")
|
|
1386
|
-
return splitLinkEntries(value);
|
|
1387
|
-
if (name === "vary")
|
|
1388
|
-
return value.split(",").map((token) => token.trim()).filter(Boolean);
|
|
1389
|
-
return [value.trim()];
|
|
1390
|
-
}
|
|
1391
|
-
function tokenKey(name, token) {
|
|
1392
|
-
return name === "vary" ? token.toLowerCase() : token;
|
|
1393
|
-
}
|
|
1394
|
-
function dedupeStaticHeaders(contents, names) {
|
|
1395
|
-
const owned = new Set(names.map((name) => name.toLowerCase()));
|
|
1396
|
-
const blocks = splitHeaderBlocks(contents);
|
|
1397
|
-
const detached = /* @__PURE__ */ new Set();
|
|
1398
|
-
const parsed = blocks.map((block) => {
|
|
1399
|
-
const headers = [];
|
|
1400
|
-
for (const line of block.text.split(/(?<=\n)/).slice(block.route === null ? 0 : 1)) {
|
|
1401
|
-
const detach = line.match(RE_DETACH_LINE);
|
|
1402
|
-
if (detach) {
|
|
1403
|
-
detached.add(detach[1].toLowerCase());
|
|
1404
|
-
continue;
|
|
1405
|
-
}
|
|
1406
|
-
const header = parseHeaderLine(line);
|
|
1407
|
-
if (!header)
|
|
1408
|
-
continue;
|
|
1409
|
-
const name = header.name.toLowerCase();
|
|
1410
|
-
if (owned.has(name))
|
|
1411
|
-
headers.push({ name, tokens: headerTokens(name, header.value) });
|
|
1412
|
-
}
|
|
1413
|
-
return headers;
|
|
1414
|
-
});
|
|
1415
|
-
const lastRuleIndex = /* @__PURE__ */ new Map();
|
|
1416
|
-
blocks.forEach((block, index) => {
|
|
1417
|
-
if (block.route !== null)
|
|
1418
|
-
lastRuleIndex.set(block.route, index);
|
|
1419
|
-
});
|
|
1420
|
-
let changed = false;
|
|
1421
|
-
const output = blocks.map((block) => {
|
|
1422
|
-
const route = block.route;
|
|
1423
|
-
if (route === null)
|
|
1424
|
-
return block.text;
|
|
1425
|
-
const provided = /* @__PURE__ */ new Map();
|
|
1426
|
-
lastRuleIndex.forEach((index, pattern) => {
|
|
1427
|
-
if (!coversPattern(pattern, route))
|
|
1428
|
-
return;
|
|
1429
|
-
for (const { name, tokens } of parsed[index]) {
|
|
1430
|
-
if (detached.has(name))
|
|
1431
|
-
continue;
|
|
1432
|
-
const values = provided.get(name) ?? /* @__PURE__ */ new Set();
|
|
1433
|
-
tokens.forEach((token) => values.add(tokenKey(name, token)));
|
|
1434
|
-
provided.set(name, values);
|
|
1435
|
-
}
|
|
1436
|
-
});
|
|
1437
|
-
if (provided.size === 0)
|
|
1438
|
-
return block.text;
|
|
1439
|
-
const [routeLine, ...lines] = block.text.split(/(?<=\n)/);
|
|
1440
|
-
let headerLines = 0;
|
|
1441
|
-
const kept = lines.flatMap((line) => {
|
|
1442
|
-
const header = parseHeaderLine(line);
|
|
1443
|
-
if (!header) {
|
|
1444
|
-
if (RE_DETACH_LINE.test(line))
|
|
1445
|
-
headerLines++;
|
|
1446
|
-
return [line];
|
|
1447
|
-
}
|
|
1448
|
-
headerLines++;
|
|
1449
|
-
const name = header.name.toLowerCase();
|
|
1450
|
-
const covered = provided.get(name);
|
|
1451
|
-
if (!covered)
|
|
1452
|
-
return [line];
|
|
1453
|
-
const tokens = headerTokens(name, header.value);
|
|
1454
|
-
const remaining = tokens.filter((token) => !covered.has(tokenKey(name, token)));
|
|
1455
|
-
if (remaining.length === tokens.length)
|
|
1456
|
-
return [line];
|
|
1457
|
-
changed = true;
|
|
1458
|
-
if (remaining.length > 0)
|
|
1459
|
-
return [header.withValue(remaining.join(", "))];
|
|
1460
|
-
headerLines--;
|
|
1461
|
-
return [];
|
|
1462
|
-
});
|
|
1463
|
-
return headerLines === 0 ? kept.join("") : `${routeLine}${kept.join("")}`;
|
|
1464
|
-
});
|
|
1465
|
-
return changed ? output.join("") : contents;
|
|
1466
|
-
}
|
|
1467
|
-
const CLOUDFLARE_STATIC_HEADER_RULE_LIMIT = 100;
|
|
1468
|
-
const RE_EXACT_MARKDOWN_ROUTE = /^\/[^*\s]*\.md[\t ]*\r?$/;
|
|
1469
|
-
function splitHeaderBlocks(contents) {
|
|
1470
|
-
const blocks = [];
|
|
1471
|
-
let current = { route: null, text: "" };
|
|
1472
|
-
for (const line of contents.split(/(?<=\n)/)) {
|
|
1473
|
-
const isRoute = /^[^\s#]/.test(line);
|
|
1474
|
-
if (isRoute) {
|
|
1475
|
-
blocks.push(current);
|
|
1476
|
-
current = { route: line.trimEnd(), text: "" };
|
|
1477
|
-
}
|
|
1478
|
-
current.text += line;
|
|
1479
|
-
}
|
|
1480
|
-
blocks.push(current);
|
|
1481
|
-
return blocks;
|
|
1482
|
-
}
|
|
1483
|
-
function enforceStaticHeaderBudget(contents, limit) {
|
|
1484
|
-
const blocks = splitHeaderBlocks(contents);
|
|
1485
|
-
const ruleCount = blocks.filter((block) => block.route !== null).length;
|
|
1486
|
-
if (ruleCount <= limit)
|
|
1487
|
-
return { contents, total: ruleCount, dropped: 0 };
|
|
1488
|
-
const kept = blocks.filter((block) => block.route === null || !RE_EXACT_MARKDOWN_ROUTE.test(block.route));
|
|
1489
|
-
return {
|
|
1490
|
-
contents: kept.map((block) => block.text).join(""),
|
|
1491
|
-
total: kept.filter((block) => block.route !== null).length,
|
|
1492
|
-
dropped: ruleCount - kept.filter((block) => block.route !== null).length
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
1618
|
const DEFAULT_MAX_OUTPUT_CHARS = 1500;
|
|
1497
1619
|
const DEFAULT_LIST_LIMIT = 20;
|
|
1498
1620
|
const DEFAULT_SEARCH_LIMIT = 10;
|
|
@@ -2121,18 +2243,17 @@ ${details}`);
|
|
|
2121
2243
|
const sitemapConfig = nuxt.options.sitemap;
|
|
2122
2244
|
const sitemapRouteRule = nuxt.options.nitro?.routeRules?.["/sitemap.xml"];
|
|
2123
2245
|
const sitemapPrerendered = !!(sitemapConfig?.zeroRuntime || sitemapRouteRule?.prerender || process.argv.includes("generate") || process.env.NUXT_GENERATE === "true");
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
if (
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
await writeFile(join(cacheDir, "secret"), runtimeSyncSecret);
|
|
2246
|
+
const secretPath = join(nuxt.options.rootDir, "node_modules/.cache/nuxt/ai-ready/secret");
|
|
2247
|
+
const appSecret = process.env.NITRO_APP_SECRET ?? process.env.NUXT_APP_SECRET ?? nuxt.options.runtimeConfig.appSecret;
|
|
2248
|
+
const legacySecret = process.env.NUXT_NUXT_AI_READY_RUNTIME_SYNC_SECRET ?? process.env.NUXT_AI_READY_RUNTIME_SYNC_SECRET ?? config.runtimeSyncSecret;
|
|
2249
|
+
if (legacySecret)
|
|
2250
|
+
logger.warn(LEGACY_RUNTIME_SYNC_SECRET_WARNING);
|
|
2251
|
+
const syncSecret = typeof appSecret === "string" && appSecret.length >= 32 ? await deriveRuntimeSyncSecret(appSecret) : appSecret === void 0 || appSecret === "" ? legacySecret : void 0;
|
|
2252
|
+
if (runtimeSyncEnabled && syncSecret) {
|
|
2253
|
+
await mkdir(dirname(secretPath), { recursive: true });
|
|
2254
|
+
await writeFile(secretPath, syncSecret, { mode: 384 });
|
|
2255
|
+
} else {
|
|
2256
|
+
await rm(secretPath, { force: true });
|
|
2136
2257
|
}
|
|
2137
2258
|
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
2138
2259
|
if (getNitroVersion(nuxt) === 3) {
|
|
@@ -2171,9 +2292,11 @@ ${details}`);
|
|
|
2171
2292
|
const externals = nitroConfig.externals ||= {};
|
|
2172
2293
|
externals.external ||= [];
|
|
2173
2294
|
externals.external.push("mdream");
|
|
2295
|
+
const rollupExternal = (nitroConfig.rollupConfig ||= {}).external ||= [];
|
|
2296
|
+
if (Array.isArray(rollupExternal))
|
|
2297
|
+
rollupExternal.push("node:sqlite");
|
|
2174
2298
|
}
|
|
2175
2299
|
if (config.cron && !nuxt.options.dev) {
|
|
2176
|
-
const cronSchedule = "*/5 * * * *";
|
|
2177
2300
|
const preset2 = String(nitroConfig.preset || "");
|
|
2178
2301
|
const isVercel = preset2 === "vercel" || preset2 === "vercel-edge";
|
|
2179
2302
|
const isCloudflarePages = preset2 === "cloudflare-pages" || preset2 === "cloudflare_pages";
|
|
@@ -2184,7 +2307,7 @@ ${details}`);
|
|
|
2184
2307
|
nitroConfig.vercel.config = nitroConfig.vercel.config || {};
|
|
2185
2308
|
nitroConfig.vercel.config.crons = nitroConfig.vercel.config.crons || [];
|
|
2186
2309
|
nitroConfig.vercel.config.crons.push({
|
|
2187
|
-
schedule:
|
|
2310
|
+
schedule: AI_READY_CRON_SCHEDULE,
|
|
2188
2311
|
path: "/__ai-ready/cron"
|
|
2189
2312
|
});
|
|
2190
2313
|
} else {
|
|
@@ -2194,8 +2317,8 @@ ${details}`);
|
|
|
2194
2317
|
handler: resolve$1("./runtime/server/tasks/ai-ready-cron")
|
|
2195
2318
|
};
|
|
2196
2319
|
nitroConfig.scheduledTasks = nitroConfig.scheduledTasks || {};
|
|
2197
|
-
nitroConfig.scheduledTasks[
|
|
2198
|
-
nitroConfig.scheduledTasks[
|
|
2320
|
+
nitroConfig.scheduledTasks[AI_READY_CRON_SCHEDULE] = nitroConfig.scheduledTasks[AI_READY_CRON_SCHEDULE] || [];
|
|
2321
|
+
nitroConfig.scheduledTasks[AI_READY_CRON_SCHEDULE].push("ai-ready:cron");
|
|
2199
2322
|
const isCloudflareWorkers = preset2.startsWith("cloudflare");
|
|
2200
2323
|
if (isCloudflareWorkers) {
|
|
2201
2324
|
nitroConfig.cloudflare = nitroConfig.cloudflare || {};
|
|
@@ -2203,8 +2326,8 @@ ${details}`);
|
|
|
2203
2326
|
nitroConfig.cloudflare.wrangler = nitroConfig.cloudflare.wrangler || {};
|
|
2204
2327
|
nitroConfig.cloudflare.wrangler.triggers = nitroConfig.cloudflare.wrangler.triggers || {};
|
|
2205
2328
|
nitroConfig.cloudflare.wrangler.triggers.crons = nitroConfig.cloudflare.wrangler.triggers.crons || [];
|
|
2206
|
-
if (!nitroConfig.cloudflare.wrangler.triggers.crons.includes(
|
|
2207
|
-
nitroConfig.cloudflare.wrangler.triggers.crons.push(
|
|
2329
|
+
if (!nitroConfig.cloudflare.wrangler.triggers.crons.includes(AI_READY_CRON_SCHEDULE)) {
|
|
2330
|
+
nitroConfig.cloudflare.wrangler.triggers.crons.push(AI_READY_CRON_SCHEDULE);
|
|
2208
2331
|
}
|
|
2209
2332
|
}
|
|
2210
2333
|
}
|
|
@@ -2322,7 +2445,7 @@ export function trackDrizzleWork(event, work) { return work }
|
|
|
2322
2445
|
batchSize: runtimeSyncConfig.batchSize ?? 50,
|
|
2323
2446
|
pruneTtl: runtimeSyncConfig.pruneTtl ?? 0
|
|
2324
2447
|
},
|
|
2325
|
-
runtimeSyncSecret,
|
|
2448
|
+
runtimeSyncSecret: legacySecret || "",
|
|
2326
2449
|
sitemapPrerendered,
|
|
2327
2450
|
i18n: i18nConfig,
|
|
2328
2451
|
ftsTokenizer,
|
|
@@ -2335,9 +2458,10 @@ export function trackDrizzleWork(event, work) { return work }
|
|
|
2335
2458
|
handler: resolve$1("./runtime/server/middleware/markdown.prerender")
|
|
2336
2459
|
});
|
|
2337
2460
|
const htmlCapturePlugin = resolve$1("./runtime/server/plugins/html-capture.prerender");
|
|
2461
|
+
const policyCapturePlugin = resolve$1(getNitroVersion(nuxt) === 3 ? "./runtime/server/plugins/content-signal-capture-nitro3.prerender" : "./runtime/server/plugins/content-signal-capture.prerender");
|
|
2338
2462
|
nuxt.hook("nitro:init", (nitro) => {
|
|
2339
2463
|
nitro.hooks.hook("prerender:config", (prerenderConfig) => {
|
|
2340
|
-
prerenderConfig.plugins = [...prerenderConfig.plugins || [], htmlCapturePlugin];
|
|
2464
|
+
prerenderConfig.plugins = [...prerenderConfig.plugins || [], htmlCapturePlugin, policyCapturePlugin];
|
|
2341
2465
|
});
|
|
2342
2466
|
});
|
|
2343
2467
|
addPlugin({
|
|
@@ -2598,11 +2722,14 @@ export function trackDrizzleWork(event, work) { return work }
|
|
|
2598
2722
|
discovery.push(`<${encodeURI(apiCatalogConfig.href)}>; rel="api-catalog"`);
|
|
2599
2723
|
mergedHeaders = mergeStaticDiscoveryHeaders(mergedHeaders, staticBaseURL, discovery);
|
|
2600
2724
|
mergedHeaders = dedupeStaticHeaders(mergedHeaders, ["Content-Type", "Link", "Vary"]);
|
|
2725
|
+
mergedHeaders = normalizeStaticContentSignalHeaders(mergedHeaders);
|
|
2601
2726
|
const budget = enforceStaticHeaderBudget(mergedHeaders, CLOUDFLARE_STATIC_HEADER_RULE_LIMIT);
|
|
2602
2727
|
if (budget.dropped > 0) {
|
|
2603
2728
|
mergedHeaders = budget.contents;
|
|
2604
2729
|
noteSharedMarkdownRule(budget.total + budget.dropped, budget.dropped);
|
|
2605
2730
|
}
|
|
2731
|
+
if (budget.total > CLOUDFLARE_STATIC_HEADER_RULE_LIMIT && /^\s+(?:content-signal\s*:|!\s*content-signal\s*$)/im.test(budget.contents))
|
|
2732
|
+
throw new Error(`Content-Signal policy rules exceed Cloudflare's ${CLOUDFLARE_STATIC_HEADER_RULE_LIMIT}-rule limit. Reduce per-page policies or use a shared policy.`);
|
|
2606
2733
|
if (budget.total > CLOUDFLARE_STATIC_HEADER_RULE_LIMIT)
|
|
2607
2734
|
logger.warn(`_headers still has ${budget.total} rules after dropping the .md rules. Cloudflare will reject the deploy until the other rules fit under ${CLOUDFLARE_STATIC_HEADER_RULE_LIMIT}.`);
|
|
2608
2735
|
}
|
|
@@ -2616,4 +2743,4 @@ export function trackDrizzleWork(event, work) { return work }
|
|
|
2616
2743
|
}
|
|
2617
2744
|
});
|
|
2618
2745
|
|
|
2619
|
-
export { AGENT_SKILLS_SCHEMA as A, MARKDOWN_LINK_AVAILABILITY_FILE as M, logger as l,
|
|
2746
|
+
export { AGENT_SKILLS_SCHEMA as A, MARKDOWN_LINK_AVAILABILITY_FILE as M, module$1 as a, logger as l, matchingStaticContentSignals as m };
|