@zedtech/zedkit-seo 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +39 -2
- package/dist/description.d.ts +17 -0
- package/dist/description.d.ts.map +1 -0
- package/dist/description.js +36 -0
- package/dist/description.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/jsonld.d.ts +31 -0
- package/dist/jsonld.d.ts.map +1 -0
- package/dist/jsonld.js +49 -0
- package/dist/jsonld.js.map +1 -0
- package/dist/llms.d.ts +17 -0
- package/dist/llms.d.ts.map +1 -0
- package/dist/llms.js +30 -0
- package/dist/llms.js.map +1 -0
- package/dist/metadata.d.ts +85 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +65 -0
- package/dist/metadata.js.map +1 -0
- package/dist/robots.d.ts +25 -0
- package/dist/robots.d.ts.map +1 -0
- package/dist/robots.js +14 -0
- package/dist/robots.js.map +1 -0
- package/dist/sitemap.d.ts +24 -0
- package/dist/sitemap.d.ts.map +1 -0
- package/dist/sitemap.js +16 -0
- package/dist/sitemap.js.map +1 -0
- package/dist/slug.d.ts +5 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +22 -0
- package/dist/slug.js.map +1 -0
- package/dist/video-sitemap.d.ts +22 -0
- package/dist/video-sitemap.d.ts.map +1 -0
- package/dist/video-sitemap.js +31 -0
- package/dist/video-sitemap.js.map +1 -0
- package/package.json +42 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zedtech-fr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zedtech/zedkit-seo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generic SEO bricks: robots, sitemaps, typed JSON-LD, page metadata,
|
|
4
|
+
llms.txt, slugs. Pure functions, options-driven, no brand, no CMS.
|
|
5
|
+
Original work by the same author.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @zedtech/zedkit-seo
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { buildRobots, buildSitemapEntries } from "@zedtech/zedkit-seo";
|
|
13
|
+
|
|
14
|
+
export async function GET() {
|
|
15
|
+
return Response.json(buildSitemapEntries({ siteUrl, locales, pages }));
|
|
16
|
+
}
|
|
17
|
+
export function robots() {
|
|
18
|
+
return buildRobots({ siteUrl });
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Return shapes follow the Next.js Metadata API (`MetadataRoute.Robots`,
|
|
23
|
+
`MetadataRoute.Sitemap`, `Metadata`) so builders drop straight into
|
|
24
|
+
`robots.ts`, `sitemap.ts`, and `generateMetadata`. JSON-LD builders
|
|
25
|
+
return `schema-dts` types. Map CMS docs to the plain `pages`/`videos`
|
|
26
|
+
inputs first — the package never talks to a CMS.
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
|
|
30
|
+
- `buildRobots({ siteUrl, extraSitemaps?, disallow?, allow? })`
|
|
31
|
+
- `buildSitemapEntries({ siteUrl, locales, pages })`
|
|
32
|
+
- `buildVideoSitemap({ videos })`, `escapeXml(value)`
|
|
33
|
+
- `buildOrganizationJsonLd`, `buildWebPageJsonLd`,
|
|
34
|
+
`buildBreadcrumbJsonLd`, `buildArticleJsonLd`
|
|
35
|
+
- `buildPageMetadata({ site, page, locale, pathname, ogImage?, robots? })`
|
|
36
|
+
- `llmsTextResponse({ body })`, `normalizeLlmsBody`, `buildLlmsFull`
|
|
37
|
+
- `truncateDescription`, `resolveDescription`, `resolveLocalizedField`
|
|
38
|
+
- `normalizeSlug`, `isDraftSlug`
|
|
39
|
+
|
|
40
|
+
See CATALOG.md for needs-first examples.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SEO_DESCRIPTION_MAX = 160;
|
|
2
|
+
/** Collapse whitespace and cap at 160 chars with an ellipsis (SERP length). */
|
|
3
|
+
export declare function truncateDescription(text: string, max?: number): string;
|
|
4
|
+
export type LocalizedField = string | Partial<Record<string, string>> | null | undefined;
|
|
5
|
+
/** Resolve a localized CMS value: exact locale, then fallbacks, then first. */
|
|
6
|
+
export declare function resolveLocalizedField(value: LocalizedField, locale: string, fallbackLocales?: string[]): string;
|
|
7
|
+
export interface DescriptionSource {
|
|
8
|
+
/** Explicit SEO description (wins when present). */
|
|
9
|
+
custom?: string | null;
|
|
10
|
+
/** Plain-text body to derive from (convert rich text to text first). */
|
|
11
|
+
bodyText?: string | null;
|
|
12
|
+
/** Localized title, last resort. */
|
|
13
|
+
title?: LocalizedField;
|
|
14
|
+
}
|
|
15
|
+
/** Description fallback chain: custom → body text → title, truncated. */
|
|
16
|
+
export declare function resolveDescription(source: DescriptionSource, locale?: string): string;
|
|
17
|
+
//# sourceMappingURL=description.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"description.d.ts","sourceRoot":"","sources":["../src/description.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAA4B,GAAG,MAAM,CAI3F;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAEzF,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,MAAM,EACd,eAAe,GAAE,MAAM,EAAW,GACjC,MAAM,CAWR;AAED,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oCAAoC;IACpC,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,SAAO,GAAG,MAAM,CAMnF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const SEO_DESCRIPTION_MAX = 160;
|
|
2
|
+
/** Collapse whitespace and cap at 160 chars with an ellipsis (SERP length). */
|
|
3
|
+
export function truncateDescription(text, max = SEO_DESCRIPTION_MAX) {
|
|
4
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
5
|
+
if (normalized.length <= max)
|
|
6
|
+
return normalized;
|
|
7
|
+
return `${normalized.slice(0, max - 1).trimEnd()}…`;
|
|
8
|
+
}
|
|
9
|
+
/** Resolve a localized CMS value: exact locale, then fallbacks, then first. */
|
|
10
|
+
export function resolveLocalizedField(value, locale, fallbackLocales = ["en"]) {
|
|
11
|
+
if (typeof value === "string")
|
|
12
|
+
return value.trim();
|
|
13
|
+
if (value === null || value === undefined)
|
|
14
|
+
return "";
|
|
15
|
+
for (const candidate of [locale, ...fallbackLocales]) {
|
|
16
|
+
const hit = value[candidate];
|
|
17
|
+
if (hit !== undefined && hit.trim().length > 0)
|
|
18
|
+
return hit.trim();
|
|
19
|
+
}
|
|
20
|
+
for (const hit of Object.values(value)) {
|
|
21
|
+
if (hit !== undefined && hit.trim().length > 0)
|
|
22
|
+
return hit.trim();
|
|
23
|
+
}
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
/** Description fallback chain: custom → body text → title, truncated. */
|
|
27
|
+
export function resolveDescription(source, locale = "en") {
|
|
28
|
+
const custom = (source.custom ?? "").trim();
|
|
29
|
+
if (custom.length > 0)
|
|
30
|
+
return truncateDescription(custom);
|
|
31
|
+
const body = (source.bodyText ?? "").trim();
|
|
32
|
+
if (body.length > 0)
|
|
33
|
+
return truncateDescription(body);
|
|
34
|
+
return truncateDescription(resolveLocalizedField(source.title, locale));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=description.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"description.js","sourceRoot":"","sources":["../src/description.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,+EAA+E;AAC/E,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,MAAc,mBAAmB;IACjF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,UAAU,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,UAAU,CAAC;IAChD,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC;AACtD,CAAC;AAID,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB,CACnC,KAAqB,EACrB,MAAc,EACd,kBAA4B,CAAC,IAAI,CAAC;IAElC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACnD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,KAAK,MAAM,SAAS,IAAI,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,CAAC;QACrD,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpE,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAWD,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAAC,MAAyB,EAAE,MAAM,GAAG,IAAI;IACzE,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,mBAAmB,CAAC,qBAAqB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAC1E,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { SEO_DESCRIPTION_MAX, resolveDescription, resolveLocalizedField, truncateDescription, type DescriptionSource, type LocalizedField, } from "./description.js";
|
|
2
|
+
export { buildArticleJsonLd, buildBreadcrumbJsonLd, buildOrganizationJsonLd, buildWebPageJsonLd, type ArticleJsonLdOptions, type BreadcrumbItem, type OrganizationJsonLdOptions, type WebPageJsonLdOptions, } from "./jsonld.js";
|
|
3
|
+
export { buildLlmsFull, llmsTextResponse, normalizeLlmsBody, type LlmsFullSection, type LlmsTextResponseOptions, } from "./llms.js";
|
|
4
|
+
export { buildPageMetadata, type BuildPageMetadataOptions, type OpenGraphImage, type PageContent, type PageMetadata, type SiteMetadata, } from "./metadata.js";
|
|
5
|
+
export { buildRobots, type BuildRobotsOptions, type RobotsConfig } from "./robots.js";
|
|
6
|
+
export { buildSitemapEntries, type BuildSitemapOptions, type SitemapEntry, type SitemapPage, } from "./sitemap.js";
|
|
7
|
+
export { isDraftSlug, normalizeSlug } from "./slug.js";
|
|
8
|
+
export { buildVideoSitemap, escapeXml, type BuildVideoSitemapOptions, type VideoSitemapEntry, } from "./video-sitemap.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { SEO_DESCRIPTION_MAX, resolveDescription, resolveLocalizedField, truncateDescription, } from "./description.js";
|
|
2
|
+
export { buildArticleJsonLd, buildBreadcrumbJsonLd, buildOrganizationJsonLd, buildWebPageJsonLd, } from "./jsonld.js";
|
|
3
|
+
export { buildLlmsFull, llmsTextResponse, normalizeLlmsBody, } from "./llms.js";
|
|
4
|
+
export { buildPageMetadata, } from "./metadata.js";
|
|
5
|
+
export { buildRobots } from "./robots.js";
|
|
6
|
+
export { buildSitemapEntries, } from "./sitemap.js";
|
|
7
|
+
export { isDraftSlug, normalizeSlug } from "./slug.js";
|
|
8
|
+
export { buildVideoSitemap, escapeXml, } from "./video-sitemap.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GAGpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,GAKnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,GAMlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAA8C,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,mBAAmB,GAIpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,SAAS,GAGV,MAAM,oBAAoB,CAAC"}
|
package/dist/jsonld.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Article, BreadcrumbList, Organization, WebPage, WithContext } from "schema-dts";
|
|
2
|
+
export interface OrganizationJsonLdOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
url: string;
|
|
5
|
+
logoUrl?: string;
|
|
6
|
+
sameAs?: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function buildOrganizationJsonLd(options: OrganizationJsonLdOptions): WithContext<Organization>;
|
|
9
|
+
export interface WebPageJsonLdOptions {
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
url: string;
|
|
13
|
+
breadcrumb?: BreadcrumbList;
|
|
14
|
+
}
|
|
15
|
+
export declare function buildWebPageJsonLd(options: WebPageJsonLdOptions): WithContext<WebPage>;
|
|
16
|
+
export interface BreadcrumbItem {
|
|
17
|
+
name: string;
|
|
18
|
+
url: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function buildBreadcrumbJsonLd(items: BreadcrumbItem[]): WithContext<BreadcrumbList>;
|
|
21
|
+
export interface ArticleJsonLdOptions {
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
url: string;
|
|
25
|
+
datePublished: string;
|
|
26
|
+
dateModified?: string;
|
|
27
|
+
authorName?: string;
|
|
28
|
+
imageUrl?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function buildArticleJsonLd(options: ArticleJsonLdOptions): WithContext<Article>;
|
|
31
|
+
//# sourceMappingURL=jsonld.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonld.d.ts","sourceRoot":"","sources":["../src/jsonld.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,YAAY,EACZ,OAAO,EACP,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,WAAW,CAAC,YAAY,CAAC,CAUrG;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CAAC,OAAO,CAAC,CAStF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,WAAW,CAAC,cAAc,CAAC,CAW1F;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CAAC,OAAO,CAAC,CActF"}
|
package/dist/jsonld.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function buildOrganizationJsonLd(options) {
|
|
2
|
+
return {
|
|
3
|
+
"@context": "https://schema.org",
|
|
4
|
+
"@type": "Organization",
|
|
5
|
+
"@id": `${options.url.replace(/\/$/, "")}/#organization`,
|
|
6
|
+
name: options.name,
|
|
7
|
+
url: options.url,
|
|
8
|
+
...(options.logoUrl === undefined ? {} : { logo: options.logoUrl }),
|
|
9
|
+
...(options.sameAs === undefined ? {} : { sameAs: options.sameAs }),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function buildWebPageJsonLd(options) {
|
|
13
|
+
return {
|
|
14
|
+
"@context": "https://schema.org",
|
|
15
|
+
"@type": "WebPage",
|
|
16
|
+
name: options.title,
|
|
17
|
+
description: options.description,
|
|
18
|
+
url: options.url,
|
|
19
|
+
...(options.breadcrumb === undefined ? {} : { breadcrumb: options.breadcrumb }),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function buildBreadcrumbJsonLd(items) {
|
|
23
|
+
return {
|
|
24
|
+
"@context": "https://schema.org",
|
|
25
|
+
"@type": "BreadcrumbList",
|
|
26
|
+
itemListElement: items.map((item, index) => ({
|
|
27
|
+
"@type": "ListItem",
|
|
28
|
+
position: index + 1,
|
|
29
|
+
name: item.name,
|
|
30
|
+
item: item.url,
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function buildArticleJsonLd(options) {
|
|
35
|
+
return {
|
|
36
|
+
"@context": "https://schema.org",
|
|
37
|
+
"@type": "Article",
|
|
38
|
+
headline: options.title,
|
|
39
|
+
description: options.description,
|
|
40
|
+
url: options.url,
|
|
41
|
+
datePublished: options.datePublished,
|
|
42
|
+
...(options.dateModified === undefined ? {} : { dateModified: options.dateModified }),
|
|
43
|
+
...(options.authorName === undefined
|
|
44
|
+
? {}
|
|
45
|
+
: { author: { "@type": "Person", name: options.authorName } }),
|
|
46
|
+
...(options.imageUrl === undefined ? {} : { image: options.imageUrl }),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=jsonld.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonld.js","sourceRoot":"","sources":["../src/jsonld.ts"],"names":[],"mappings":"AAeA,MAAM,UAAU,uBAAuB,CAAC,OAAkC;IACxE,OAAO;QACL,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB;QACxD,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACnE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC;AASD,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,OAAO;QACL,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,OAAO,CAAC,KAAK;QACnB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;KAChF,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,qBAAqB,CAAC,KAAuB;IAC3D,OAAO;QACL,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,gBAAgB;QACzB,eAAe,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,OAAO,EAAE,UAAU;YACnB,QAAQ,EAAE,KAAK,GAAG,CAAC;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,GAAG;SACf,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAYD,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,OAAO;QACL,UAAU,EAAE,oBAAoB;QAChC,OAAO,EAAE,SAAS;QAClB,QAAQ,EAAE,OAAO,CAAC,KAAK;QACvB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;QACrF,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS;YAClC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;KACvE,CAAC;AACJ,CAAC"}
|
package/dist/llms.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Normalize CMS-stored llms.txt content (CRLF + surrounding blank lines). */
|
|
2
|
+
export declare function normalizeLlmsBody(body: string): string;
|
|
3
|
+
export interface LlmsTextResponseOptions {
|
|
4
|
+
/** Normalized body; empty answers 404 (nothing published yet). */
|
|
5
|
+
body: string;
|
|
6
|
+
/** Cache for served bodies. Defaults to one minute. */
|
|
7
|
+
cacheControl?: string;
|
|
8
|
+
}
|
|
9
|
+
/** `text/plain` Response for an llms.txt route (200 with body, else 404). */
|
|
10
|
+
export declare function llmsTextResponse(options: LlmsTextResponseOptions): Response;
|
|
11
|
+
export interface LlmsFullSection {
|
|
12
|
+
title: string;
|
|
13
|
+
body: string;
|
|
14
|
+
}
|
|
15
|
+
/** Join titled sections into one llms-full document (skips empty bodies). */
|
|
16
|
+
export declare function buildLlmsFull(sections: LlmsFullSection[]): string;
|
|
17
|
+
//# sourceMappingURL=llms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../src/llms.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,QAAQ,CAe3E;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAMjE"}
|
package/dist/llms.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Normalize CMS-stored llms.txt content (CRLF + surrounding blank lines). */
|
|
2
|
+
export function normalizeLlmsBody(body) {
|
|
3
|
+
return body.replace(/\r\n/g, "\n").trim();
|
|
4
|
+
}
|
|
5
|
+
/** `text/plain` Response for an llms.txt route (200 with body, else 404). */
|
|
6
|
+
export function llmsTextResponse(options) {
|
|
7
|
+
const body = normalizeLlmsBody(options.body);
|
|
8
|
+
if (body.length === 0) {
|
|
9
|
+
return new Response("Not Found", {
|
|
10
|
+
status: 404,
|
|
11
|
+
headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return new Response(`${body}\n`, {
|
|
15
|
+
status: 200,
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
18
|
+
"Cache-Control": options.cacheControl ?? "public, max-age=60",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** Join titled sections into one llms-full document (skips empty bodies). */
|
|
23
|
+
export function buildLlmsFull(sections) {
|
|
24
|
+
return sections
|
|
25
|
+
.map((section) => ({ title: section.title.trim(), body: normalizeLlmsBody(section.body) }))
|
|
26
|
+
.filter((section) => section.title.length > 0 && section.body.length > 0)
|
|
27
|
+
.map((section) => `# ${section.title}\n\n${section.body}`)
|
|
28
|
+
.join("\n\n");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=llms.js.map
|
package/dist/llms.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms.js","sourceRoot":"","sources":["../src/llms.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5C,CAAC;AASD,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE;YAC/B,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,2BAA2B,EAAE,eAAe,EAAE,UAAU,EAAE;SACtF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,GAAG,IAAI,IAAI,EAAE;QAC/B,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACP,cAAc,EAAE,2BAA2B;YAC3C,eAAe,EAAE,OAAO,CAAC,YAAY,IAAI,oBAAoB;SAC9D;KACF,CAAC,CAAC;AACL,CAAC;AAOD,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAAC,QAA2B;IACvD,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC1F,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SACxE,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,KAAK,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;SACzD,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page metadata, shaped for Next.js `Metadata` (generateMetadata return).
|
|
3
|
+
* Structural on purpose: no Next dependency, assignable where used.
|
|
4
|
+
*/
|
|
5
|
+
export interface PageMetadata {
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
keywords?: string[];
|
|
9
|
+
authors?: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
}>;
|
|
12
|
+
creator?: string;
|
|
13
|
+
publisher?: string;
|
|
14
|
+
metadataBase?: URL;
|
|
15
|
+
alternates?: {
|
|
16
|
+
canonical?: string;
|
|
17
|
+
languages?: Record<string, string>;
|
|
18
|
+
};
|
|
19
|
+
openGraph?: {
|
|
20
|
+
type: "website" | "article";
|
|
21
|
+
locale?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
siteName?: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
images?: Array<{
|
|
27
|
+
url: string;
|
|
28
|
+
width?: number;
|
|
29
|
+
height?: number;
|
|
30
|
+
alt?: string;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
twitter?: {
|
|
34
|
+
card: "summary" | "summary_large_image";
|
|
35
|
+
title?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
images?: string[];
|
|
38
|
+
};
|
|
39
|
+
robots?: {
|
|
40
|
+
index: boolean;
|
|
41
|
+
follow: boolean;
|
|
42
|
+
googleBot?: {
|
|
43
|
+
index: boolean;
|
|
44
|
+
follow: boolean;
|
|
45
|
+
"max-video-preview"?: number;
|
|
46
|
+
"max-image-preview"?: "none" | "standard" | "large";
|
|
47
|
+
"max-snippet"?: number;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface SiteMetadata {
|
|
52
|
+
name: string;
|
|
53
|
+
url: string;
|
|
54
|
+
defaultLocale: string;
|
|
55
|
+
locales: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface PageContent {
|
|
58
|
+
title: string;
|
|
59
|
+
description: string;
|
|
60
|
+
keywords?: string[];
|
|
61
|
+
}
|
|
62
|
+
export interface OpenGraphImage {
|
|
63
|
+
url: string;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
}
|
|
67
|
+
export interface BuildPageMetadataOptions {
|
|
68
|
+
site: SiteMetadata;
|
|
69
|
+
page: PageContent;
|
|
70
|
+
/** Active locale, e.g. `"en"`. */
|
|
71
|
+
locale: string;
|
|
72
|
+
/** Full pathname including the locale prefix, e.g. `/en/work/slug`. */
|
|
73
|
+
pathname: string;
|
|
74
|
+
/** Brand or page social image. */
|
|
75
|
+
ogImage?: OpenGraphImage;
|
|
76
|
+
/** Open Graph locale tag, e.g. `"en_US"`. Defaults to `<locale>_<LOCALE>`. */
|
|
77
|
+
openGraphLocale?: string;
|
|
78
|
+
/** Page-level robots overrides (e.g. `{ index: false }` for drafts). */
|
|
79
|
+
robots?: {
|
|
80
|
+
index?: boolean;
|
|
81
|
+
follow?: boolean;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export declare function buildPageMetadata(options: BuildPageMetadataOptions): PageMetadata;
|
|
85
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,UAAU,CAAC,EAAE;QACX,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;IACF,SAAS,CAAC,EAAE;QACV,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAChF,CAAC;IACF,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,SAAS,GAAG,qBAAqB,CAAC;QACxC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,EAAE,OAAO,CAAC;QACf,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,CAAC,EAAE;YACV,KAAK,EAAE,OAAO,CAAC;YACf,MAAM,EAAE,OAAO,CAAC;YAChB,mBAAmB,CAAC,EAAE,MAAM,CAAC;YAC7B,mBAAmB,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;YACpD,aAAa,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAChD;AAWD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,YAAY,CAuDjF"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function stripLocale(pathname, locales) {
|
|
2
|
+
for (const locale of locales) {
|
|
3
|
+
if (pathname === `/${locale}` || pathname.startsWith(`/${locale}/`)) {
|
|
4
|
+
return pathname.slice(locale.length + 1) || "/";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
return pathname || "/";
|
|
8
|
+
}
|
|
9
|
+
export function buildPageMetadata(options) {
|
|
10
|
+
const { site, page, locale } = options;
|
|
11
|
+
const barePath = stripLocale(options.pathname, site.locales);
|
|
12
|
+
const suffix = barePath === "/" ? "" : barePath;
|
|
13
|
+
const languages = {};
|
|
14
|
+
for (const available of site.locales) {
|
|
15
|
+
languages[available] = `/${available}${suffix}`;
|
|
16
|
+
}
|
|
17
|
+
languages["x-default"] = `/${site.defaultLocale}${suffix}`;
|
|
18
|
+
const index = options.robots?.index ?? true;
|
|
19
|
+
const follow = options.robots?.follow ?? true;
|
|
20
|
+
const image = options.ogImage;
|
|
21
|
+
return {
|
|
22
|
+
title: page.title,
|
|
23
|
+
description: page.description,
|
|
24
|
+
...(page.keywords === undefined ? {} : { keywords: page.keywords }),
|
|
25
|
+
authors: [{ name: site.name }],
|
|
26
|
+
creator: site.name,
|
|
27
|
+
publisher: site.name,
|
|
28
|
+
metadataBase: new URL(site.url),
|
|
29
|
+
alternates: {
|
|
30
|
+
canonical: `/${locale}${suffix}`,
|
|
31
|
+
languages,
|
|
32
|
+
},
|
|
33
|
+
openGraph: {
|
|
34
|
+
type: "website",
|
|
35
|
+
locale: options.openGraphLocale ?? `${locale}_${locale.toUpperCase()}`,
|
|
36
|
+
url: `${site.url.replace(/\/$/, "")}/${locale}${suffix}`,
|
|
37
|
+
siteName: site.name,
|
|
38
|
+
title: page.title,
|
|
39
|
+
description: page.description,
|
|
40
|
+
...(image === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: {
|
|
43
|
+
images: [{ url: image.url, width: image.width, height: image.height, alt: site.name }],
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
twitter: {
|
|
47
|
+
card: "summary_large_image",
|
|
48
|
+
title: page.title,
|
|
49
|
+
description: page.description,
|
|
50
|
+
...(image === undefined ? {} : { images: [image.url] }),
|
|
51
|
+
},
|
|
52
|
+
robots: {
|
|
53
|
+
index,
|
|
54
|
+
follow,
|
|
55
|
+
googleBot: {
|
|
56
|
+
index,
|
|
57
|
+
follow,
|
|
58
|
+
"max-video-preview": -1,
|
|
59
|
+
"max-image-preview": "large",
|
|
60
|
+
"max-snippet": -1,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/robots.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** robots.txt content, shaped for Next.js `MetadataRoute.Robots` routes. */
|
|
2
|
+
export interface RobotsConfig {
|
|
3
|
+
rules: {
|
|
4
|
+
userAgent: string | string[];
|
|
5
|
+
allow?: string | string[];
|
|
6
|
+
disallow?: string | string[];
|
|
7
|
+
} | Array<{
|
|
8
|
+
userAgent: string | string[];
|
|
9
|
+
allow?: string | string[];
|
|
10
|
+
disallow?: string | string[];
|
|
11
|
+
}>;
|
|
12
|
+
sitemap?: string | string[];
|
|
13
|
+
}
|
|
14
|
+
export interface BuildRobotsOptions {
|
|
15
|
+
/** Absolute site URL, e.g. `https://example.com` (trailing slash trimmed). */
|
|
16
|
+
siteUrl: string;
|
|
17
|
+
/** Extra sitemap URLs besides the default `/sitemap.xml` (e.g. a video sitemap). */
|
|
18
|
+
extraSitemaps?: string[];
|
|
19
|
+
/** Paths crawlers may not visit. Defaults to the admin area. */
|
|
20
|
+
disallow?: string[];
|
|
21
|
+
/** Paths crawlers may visit. Defaults to `/`. */
|
|
22
|
+
allow?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function buildRobots(options: BuildRobotsOptions): RobotsConfig;
|
|
25
|
+
//# sourceMappingURL=robots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots.d.ts","sourceRoot":"","sources":["../src/robots.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KAC9B,GAAG,KAAK,CAAC;QACR,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KAC9B,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,YAAY,CAYrE"}
|
package/dist/robots.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function buildRobots(options) {
|
|
2
|
+
const base = options.siteUrl.replace(/\/$/, "");
|
|
3
|
+
const extra = options.extraSitemaps ?? [];
|
|
4
|
+
const sitemap = extra.length === 0 ? `${base}/sitemap.xml` : [`${base}/sitemap.xml`, ...extra];
|
|
5
|
+
return {
|
|
6
|
+
rules: {
|
|
7
|
+
userAgent: "*",
|
|
8
|
+
allow: options.allow ?? "/",
|
|
9
|
+
disallow: options.disallow ?? "/admin/",
|
|
10
|
+
},
|
|
11
|
+
sitemap,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=robots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots.js","sourceRoot":"","sources":["../src/robots.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,cAAc,EAAE,GAAG,KAAK,CAAC,CAAC;IAC/F,OAAO;QACL,KAAK,EAAE;YACL,SAAS,EAAE,GAAG;YACd,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG;YAC3B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS;SACxC;QACD,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Sitemap entry, shaped for Next.js `MetadataRoute.Sitemap` routes. */
|
|
2
|
+
export interface SitemapEntry {
|
|
3
|
+
url: string;
|
|
4
|
+
lastModified?: string | Date;
|
|
5
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
6
|
+
priority?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SitemapPage {
|
|
9
|
+
/** Locale-less path: `""` for the home page, else `/path` or `/section/slug`. */
|
|
10
|
+
path: string;
|
|
11
|
+
lastModified?: string | Date;
|
|
12
|
+
changeFrequency?: SitemapEntry["changeFrequency"];
|
|
13
|
+
priority?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface BuildSitemapOptions {
|
|
16
|
+
/** Absolute site URL, e.g. `https://example.com` (trailing slash trimmed). */
|
|
17
|
+
siteUrl: string;
|
|
18
|
+
/** Locales to prefix every page with, e.g. `["en", "fr"]`. */
|
|
19
|
+
locales: string[];
|
|
20
|
+
/** Pages to list under each locale. Map CMS docs to this shape first. */
|
|
21
|
+
pages: SitemapPage[];
|
|
22
|
+
}
|
|
23
|
+
export declare function buildSitemapEntries(options: BuildSitemapOptions): SitemapEntry[];
|
|
24
|
+
//# sourceMappingURL=sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,EAAE,CAchF"}
|
package/dist/sitemap.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function buildSitemapEntries(options) {
|
|
2
|
+
const base = options.siteUrl.replace(/\/$/, "");
|
|
3
|
+
const entries = [];
|
|
4
|
+
for (const locale of options.locales) {
|
|
5
|
+
for (const page of options.pages) {
|
|
6
|
+
entries.push({
|
|
7
|
+
url: `${base}/${locale}${page.path}`,
|
|
8
|
+
...(page.lastModified === undefined ? {} : { lastModified: page.lastModified }),
|
|
9
|
+
...(page.changeFrequency === undefined ? {} : { changeFrequency: page.changeFrequency }),
|
|
10
|
+
...(page.priority === undefined ? {} : { priority: page.priority }),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return entries;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=sitemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,mBAAmB,CAAC,OAA4B;IAC9D,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC;gBACX,GAAG,EAAE,GAAG,IAAI,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE;gBACpC,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC/E,GAAG,CAAC,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;gBACxF,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;aACpE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/slug.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Lowercase, dashed, ASCII slug (accents stripped via NFKD). */
|
|
2
|
+
export declare function normalizeSlug(value: string): string;
|
|
3
|
+
/** Draft placeholder slugs (`""`, `untitled`, `untitled-N`, `draft-N`). */
|
|
4
|
+
export declare function isDraftSlug(slug: string | null | undefined): boolean;
|
|
5
|
+
//# sourceMappingURL=slug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../src/slug.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnD;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKpE"}
|
package/dist/slug.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Lowercase, dashed, ASCII slug (accents stripped via NFKD). */
|
|
2
|
+
export function normalizeSlug(value) {
|
|
3
|
+
return value
|
|
4
|
+
.normalize("NFKD")
|
|
5
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/\s+/g, "-")
|
|
8
|
+
.replace(/[^\w-]+/g, "")
|
|
9
|
+
.replace(/--+/g, "-")
|
|
10
|
+
.replace(/^-+/, "")
|
|
11
|
+
.replace(/-+$/, "");
|
|
12
|
+
}
|
|
13
|
+
/** Draft placeholder slugs (`""`, `untitled`, `untitled-N`, `draft-N`). */
|
|
14
|
+
export function isDraftSlug(slug) {
|
|
15
|
+
const value = (slug ?? "").trim().toLowerCase();
|
|
16
|
+
if (value.length === 0)
|
|
17
|
+
return true;
|
|
18
|
+
if (value === "untitled")
|
|
19
|
+
return true;
|
|
20
|
+
return /^untitled-\d+$/.test(value) || /^draft-\d+$/.test(value);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=slug.js.map
|
package/dist/slug.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.js","sourceRoot":"","sources":["../src/slug.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,WAAW,EAAE;SACb,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,MAAM,KAAK,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function escapeXml(value: string): string;
|
|
2
|
+
export interface VideoSitemapEntry {
|
|
3
|
+
/** Absolute URL of the page embedding the video. */
|
|
4
|
+
pageUrl: string;
|
|
5
|
+
/** Absolute URL of the thumbnail image. */
|
|
6
|
+
thumbnailUrl: string;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
/** Direct media file URL, when the video is self-hosted. */
|
|
10
|
+
contentUrl?: string;
|
|
11
|
+
/** Player page URL (embeds allowed), for hosted videos. */
|
|
12
|
+
playerUrl?: string;
|
|
13
|
+
/** Publication date, `YYYY-MM-DD`. */
|
|
14
|
+
publicationDate?: string;
|
|
15
|
+
familyFriendly?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface BuildVideoSitemapOptions {
|
|
18
|
+
videos: VideoSitemapEntry[];
|
|
19
|
+
}
|
|
20
|
+
/** Google video sitemap XML (`sitemap-video/1.1` namespace). */
|
|
21
|
+
export declare function buildVideoSitemap(options: BuildVideoSitemapOptions): string;
|
|
22
|
+
//# sourceMappingURL=video-sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-sitemap.d.ts","sourceRoot":"","sources":["../src/video-sitemap.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAsB3E"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function escapeXml(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/&/g, "&")
|
|
4
|
+
.replace(/</g, "<")
|
|
5
|
+
.replace(/>/g, ">")
|
|
6
|
+
.replace(/"/g, """)
|
|
7
|
+
.replace(/'/g, "'");
|
|
8
|
+
}
|
|
9
|
+
/** Google video sitemap XML (`sitemap-video/1.1` namespace). */
|
|
10
|
+
export function buildVideoSitemap(options) {
|
|
11
|
+
const urls = options.videos.map((video) => {
|
|
12
|
+
const tags = [
|
|
13
|
+
` <video:thumbnail_loc>${escapeXml(video.thumbnailUrl)}</video:thumbnail_loc>`,
|
|
14
|
+
` <video:title>${escapeXml(video.title)}</video:title>`,
|
|
15
|
+
` <video:description>${escapeXml(video.description)}</video:description>`,
|
|
16
|
+
];
|
|
17
|
+
if (video.contentUrl !== undefined) {
|
|
18
|
+
tags.push(` <video:content_loc>${escapeXml(video.contentUrl)}</video:content_loc>`);
|
|
19
|
+
}
|
|
20
|
+
if (video.playerUrl !== undefined) {
|
|
21
|
+
tags.push(` <video:player_loc allow_embed="yes">${escapeXml(video.playerUrl)}</video:player_loc>`);
|
|
22
|
+
}
|
|
23
|
+
if (video.publicationDate !== undefined) {
|
|
24
|
+
tags.push(` <video:publication_date>${escapeXml(video.publicationDate)}</video:publication_date>`);
|
|
25
|
+
}
|
|
26
|
+
tags.push(` <video:family_friendly>${video.familyFriendly === false ? "no" : "yes"}</video:family_friendly>`);
|
|
27
|
+
return ` <url>\n <loc>${escapeXml(video.pageUrl)}</loc>\n <video:video>\n${tags.join("\n")}\n </video:video>\n </url>`;
|
|
28
|
+
});
|
|
29
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset\n xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"\n xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"\n>\n${urls.join("\n")}\n</urlset>`;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=video-sitemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-sitemap.js","sourceRoot":"","sources":["../src/video-sitemap.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAsBD,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,OAAiC;IACjE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG;YACX,8BAA8B,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,wBAAwB;YACnF,sBAAsB,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB;YAC5D,4BAA4B,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,sBAAsB;SAC/E,CAAC;QACF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,4BAA4B,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CACP,6CAA6C,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,qBAAqB,CAC7F,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,iCAAiC,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC,2BAA2B,CAAC,CAAC;QAC1G,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,gCAAgC,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,0BAA0B,CAAC,CAAC;QACnH,OAAO,qBAAqB,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,8BAA8B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC,CAAC;IACpI,CAAC,CAAC,CAAC;IACH,OAAO,+KAA+K,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;AACrN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zedtech/zedkit-seo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"registry": "https://registry.npmjs.org/"
|
|
7
|
+
},
|
|
8
|
+
"description": "Generic SEO bricks: robots, sitemaps, typed JSON-LD, metadata, llms.txt, slugs.",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/zedtech-fr/zedkit",
|
|
13
|
+
"directory": "packages/seo"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"imports": {
|
|
18
|
+
"#*": "./src/*"
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"schema-dts": "2.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "5.9.3",
|
|
37
|
+
"vitest": "2.1.9"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc -p tsconfig.json",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"typecheck": "tsc --noEmit -p tsconfig.tests.json",
|
|
43
|
+
"lint": "eslint src tests"
|
|
44
|
+
}
|
|
6
45
|
}
|