@fullstackdatasolutions/articles 1.3.0 → 1.4.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/CHANGELOG.md +16 -0
- package/README.md +12 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +23 -25
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +16 -0
- package/dist/nextjs.d.ts +16 -0
- package/dist/nextjs.js +23 -25
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +23 -8
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +16 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +23 -8
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/server-articles.test.ts +70 -0
- package/src/articlesConfig.ts +16 -0
- package/src/server-articles.ts +32 -5
package/dist/server.d.cts
CHANGED
|
@@ -599,6 +599,22 @@ interface ArticlesConfig {
|
|
|
599
599
|
* a config is available (i.e. via `getArticleMarkdownResponse`).
|
|
600
600
|
*/
|
|
601
601
|
markdownTwinHeader?: boolean;
|
|
602
|
+
/**
|
|
603
|
+
* Set to `false` to stop serving the `category/`, `authors/`, and `series/`
|
|
604
|
+
* listing twins with `X-Robots-Tag: noindex`. Default: `true`.
|
|
605
|
+
*
|
|
606
|
+
* Only the listing twins are configurable. Article twins are always
|
|
607
|
+
* `noindex`: an article twin is the same text as exactly one HTML article,
|
|
608
|
+
* so indexing it can only ever split that article's own signal between two
|
|
609
|
+
* URLs. A listing twin is a different case - it is a generated index of a
|
|
610
|
+
* category or author rather than a copy of one page - so a site may have a
|
|
611
|
+
* reason to let it rank, and this leaves that open.
|
|
612
|
+
*
|
|
613
|
+
* Either way AI crawlers can still fetch every twin; `noindex` only removes
|
|
614
|
+
* them from search results, and never blocks the fetch that is the point of
|
|
615
|
+
* publishing them.
|
|
616
|
+
*/
|
|
617
|
+
listingTwinNoindex?: boolean;
|
|
602
618
|
/**
|
|
603
619
|
* Vendor-neutral event callback (Phase 27F). Fired by components/hooks at
|
|
604
620
|
* meaningful reader-journey moments (see `ArticleEvent` in `events.ts`).
|
package/dist/server.d.ts
CHANGED
|
@@ -599,6 +599,22 @@ interface ArticlesConfig {
|
|
|
599
599
|
* a config is available (i.e. via `getArticleMarkdownResponse`).
|
|
600
600
|
*/
|
|
601
601
|
markdownTwinHeader?: boolean;
|
|
602
|
+
/**
|
|
603
|
+
* Set to `false` to stop serving the `category/`, `authors/`, and `series/`
|
|
604
|
+
* listing twins with `X-Robots-Tag: noindex`. Default: `true`.
|
|
605
|
+
*
|
|
606
|
+
* Only the listing twins are configurable. Article twins are always
|
|
607
|
+
* `noindex`: an article twin is the same text as exactly one HTML article,
|
|
608
|
+
* so indexing it can only ever split that article's own signal between two
|
|
609
|
+
* URLs. A listing twin is a different case - it is a generated index of a
|
|
610
|
+
* category or author rather than a copy of one page - so a site may have a
|
|
611
|
+
* reason to let it rank, and this leaves that open.
|
|
612
|
+
*
|
|
613
|
+
* Either way AI crawlers can still fetch every twin; `noindex` only removes
|
|
614
|
+
* them from search results, and never blocks the fetch that is the point of
|
|
615
|
+
* publishing them.
|
|
616
|
+
*/
|
|
617
|
+
listingTwinNoindex?: boolean;
|
|
602
618
|
/**
|
|
603
619
|
* Vendor-neutral event callback (Phase 27F). Fired by components/hooks at
|
|
604
620
|
* meaningful reader-journey moments (see `ArticleEvent` in `events.ts`).
|
package/dist/server.js
CHANGED
|
@@ -1025,15 +1025,17 @@ function getMarkdownTwinResponse(slug, config, options) {
|
|
|
1025
1025
|
if (listing !== void 0) {
|
|
1026
1026
|
if (listing === null) return new Response("Not Found", { status: 404 });
|
|
1027
1027
|
reportAiCrawl(slug, config, options == null ? void 0 : options.headers);
|
|
1028
|
-
return new Response(listing, { headers:
|
|
1028
|
+
return new Response(listing, { headers: listingMarkdownHeaders(config) });
|
|
1029
1029
|
}
|
|
1030
1030
|
return getArticleMarkdownResponse(slug, config, options);
|
|
1031
1031
|
});
|
|
1032
1032
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1033
|
+
function listingMarkdownHeaders(config) {
|
|
1034
|
+
return __spreadValues({
|
|
1035
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
1036
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600"
|
|
1037
|
+
}, config.listingTwinNoindex !== false && { "X-Robots-Tag": "noindex" });
|
|
1038
|
+
}
|
|
1037
1039
|
function resolveListingMarkdown(slug, config) {
|
|
1038
1040
|
return __async(this, null, function* () {
|
|
1039
1041
|
const [prefix, ...rest] = slug.split("/");
|
|
@@ -1052,11 +1054,24 @@ function getArticleMarkdownResponse(slug, config, options) {
|
|
|
1052
1054
|
const article = yield getArticleMetadata(slug, config);
|
|
1053
1055
|
reportAiCrawl(slug, config, options == null ? void 0 : options.headers);
|
|
1054
1056
|
const body = article && config.markdownTwinHeader !== false ? `${buildMarkdownTwinHeader(article, config, markdown)}${markdown.trimStart()}` : markdown;
|
|
1057
|
+
const canonicalUrl = `${config.siteUrl.replace(/\/$/, "")}/articles/${slug}`;
|
|
1055
1058
|
return new Response(body, {
|
|
1056
|
-
headers:
|
|
1059
|
+
headers: {
|
|
1057
1060
|
"Content-Type": "text/markdown; charset=utf-8",
|
|
1058
|
-
"Cache-Control": "public, max-age=3600, s-maxage=3600"
|
|
1059
|
-
|
|
1061
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
1062
|
+
Link: `<${canonicalUrl}>; rel="canonical"`,
|
|
1063
|
+
// A twin is a second representation of a page that is already indexed,
|
|
1064
|
+
// so it should not compete with that page in a search index - on a large
|
|
1065
|
+
// corpus it doubles the crawlable URL count with near-duplicate content.
|
|
1066
|
+
// noindex rather than a robots.txt disallow: AI crawlers still need to
|
|
1067
|
+
// fetch these, and a disallow would block the fetch that is the whole
|
|
1068
|
+
// point of publishing them. Set here rather than in the consuming app
|
|
1069
|
+
// because the `.md` path is typically a rewrite, so a host-level header
|
|
1070
|
+
// rule keyed on `.md` never sees the request. Not configurable: an
|
|
1071
|
+
// article twin is the same text as exactly one HTML article, so indexing
|
|
1072
|
+
// it can only ever split that article's own signal across two URLs.
|
|
1073
|
+
"X-Robots-Tag": "noindex"
|
|
1074
|
+
}
|
|
1060
1075
|
});
|
|
1061
1076
|
});
|
|
1062
1077
|
}
|