@hagicode/hagilight-starlight 0.1.0-dev.148.1.e0c630e

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.
Files changed (81) hide show
  1. package/AIDisclosureNotice.astro +12 -0
  2. package/ArticlePromotion.astro +630 -0
  3. package/ArticlePromotionPicker.astro +9 -0
  4. package/ContentLayoutToggle.astro +31 -0
  5. package/Footer.astro +201 -0
  6. package/Header.astro +131 -0
  7. package/LanguageChooser.astro +386 -0
  8. package/MarkdownContent.astro +45 -0
  9. package/NotFoundHero.astro +44 -0
  10. package/PageTitle.astro +7 -0
  11. package/PromotoFooter.astro +7 -0
  12. package/README.md +178 -0
  13. package/SEOHead.astro +67 -0
  14. package/ThemeSelect.astro +176 -0
  15. package/assets/showcase/hero-avatar.webp +0 -0
  16. package/assets/showcase/heroes.webp +0 -0
  17. package/assets/showcase/manifest.json +333 -0
  18. package/assets/showcase/pillar-efficient.svg +1 -0
  19. package/assets/showcase/pillar-fun.svg +1 -0
  20. package/assets/showcase/pillar-smart.svg +1 -0
  21. package/assets/showcase/proposal-workflow.webp +0 -0
  22. package/assets/showcase/workbench.webp +0 -0
  23. package/content-width.css +81 -0
  24. package/dist/ai-disclosures.d.ts +31 -0
  25. package/dist/ai-disclosures.js +105 -0
  26. package/dist/article-promotion-copy/de-DE.d.ts +34 -0
  27. package/dist/article-promotion-copy/de-DE.js +43 -0
  28. package/dist/article-promotion-copy/en-US.d.ts +34 -0
  29. package/dist/article-promotion-copy/en-US.js +43 -0
  30. package/dist/article-promotion-copy/es-ES.d.ts +34 -0
  31. package/dist/article-promotion-copy/es-ES.js +43 -0
  32. package/dist/article-promotion-copy/fr-FR.d.ts +34 -0
  33. package/dist/article-promotion-copy/fr-FR.js +43 -0
  34. package/dist/article-promotion-copy/index.d.ts +32 -0
  35. package/dist/article-promotion-copy/index.js +50 -0
  36. package/dist/article-promotion-copy/ja-JP.d.ts +34 -0
  37. package/dist/article-promotion-copy/ja-JP.js +43 -0
  38. package/dist/article-promotion-copy/ko-KR.d.ts +34 -0
  39. package/dist/article-promotion-copy/ko-KR.js +43 -0
  40. package/dist/article-promotion-copy/pt-BR.d.ts +34 -0
  41. package/dist/article-promotion-copy/pt-BR.js +43 -0
  42. package/dist/article-promotion-copy/ru-RU.d.ts +34 -0
  43. package/dist/article-promotion-copy/ru-RU.js +43 -0
  44. package/dist/article-promotion-copy/zh-CN.d.ts +34 -0
  45. package/dist/article-promotion-copy/zh-CN.js +43 -0
  46. package/dist/article-promotion-copy/zh-Hant.d.ts +34 -0
  47. package/dist/article-promotion-copy/zh-Hant.js +43 -0
  48. package/dist/article-promotion.d.ts +40 -0
  49. package/dist/article-promotion.js +14 -0
  50. package/dist/content-width-i18n.d.ts +6 -0
  51. package/dist/content-width-i18n.js +15 -0
  52. package/dist/content-width.d.ts +5 -0
  53. package/dist/content-width.js +42 -0
  54. package/dist/index.d.ts +105 -0
  55. package/dist/index.js +583 -0
  56. package/dist/language-routing.d.ts +18 -0
  57. package/dist/language-routing.js +95 -0
  58. package/dist/locales.d.ts +48 -0
  59. package/dist/locales.js +13 -0
  60. package/dist/not-found.d.ts +9 -0
  61. package/dist/not-found.js +28 -0
  62. package/dist/rss-renderer.d.ts +14 -0
  63. package/dist/rss-renderer.js +30 -0
  64. package/dist/rss-utils.d.ts +27 -0
  65. package/dist/rss-utils.js +65 -0
  66. package/dist/schema.d.ts +32 -0
  67. package/dist/schema.js +25 -0
  68. package/dist/seo-utils.d.ts +74 -0
  69. package/dist/seo-utils.js +219 -0
  70. package/dist/showcase.d.ts +6 -0
  71. package/dist/showcase.js +5 -0
  72. package/dist/site-metadata.d.ts +38 -0
  73. package/dist/site-metadata.js +109 -0
  74. package/dist/themes.d.ts +43 -0
  75. package/dist/themes.js +156 -0
  76. package/dist/windows-download.d.ts +4 -0
  77. package/dist/windows-download.js +22 -0
  78. package/package.json +88 -0
  79. package/rss.[language].xml.ts +20 -0
  80. package/rss.xml.ts +15 -0
  81. package/themes.css +160 -0
@@ -0,0 +1,95 @@
1
+ import { locales } from './locales.js';
2
+ export const DEFAULT_LANGUAGE_OPTIONS = Object.entries(locales).map(([code, locale]) => ({
3
+ code,
4
+ label: locale.label,
5
+ lang: locale.lang,
6
+ }));
7
+ const normalizeLang = (lang) => lang?.replaceAll('_', '-').toLowerCase();
8
+ export function getConfiguredLanguageOptions(configuredLocales, currentLocale) {
9
+ const catalog = DEFAULT_LANGUAGE_OPTIONS;
10
+ return Object.entries(configuredLocales ?? {})
11
+ .map(([code, locale]) => {
12
+ const lang = typeof locale === 'string' ? locale : locale?.lang ?? code;
13
+ const catalogEntry = catalog.find((entry) => entry.code === code || normalizeLang(entry.lang) === normalizeLang(lang));
14
+ return {
15
+ code,
16
+ label: typeof locale === 'object' && locale?.label
17
+ ? locale.label
18
+ : catalogEntry?.label ?? code,
19
+ lang,
20
+ selected: code === currentLocale,
21
+ catalogOrder: catalogEntry ? catalog.indexOf(catalogEntry) : catalog.length,
22
+ };
23
+ })
24
+ .sort((left, right) => left.catalogOrder - right.catalogOrder)
25
+ .map(({ catalogOrder: _catalogOrder, ...option }) => option);
26
+ }
27
+ export function getKeyboardTargetIndex(index, key, count) {
28
+ if (count < 1)
29
+ return -1;
30
+ switch (key) {
31
+ case 'ArrowDown':
32
+ case 'ArrowRight':
33
+ return (index + 1) % count;
34
+ case 'ArrowUp':
35
+ case 'ArrowLeft':
36
+ return (index - 1 + count) % count;
37
+ case 'Home':
38
+ return 0;
39
+ case 'End':
40
+ return count - 1;
41
+ default:
42
+ return index;
43
+ }
44
+ }
45
+ export function buildLocaleNavigationTarget(currentUrl, targetLocale, configuredLocales, basePath = '/', trailingSlash = 'ignore') {
46
+ const targetUrl = new URL(currentUrl);
47
+ const base = basePath.replace(/\/+$/u, '');
48
+ const hasBase = base === ''
49
+ ? true
50
+ : targetUrl.pathname === base || targetUrl.pathname.startsWith(`${base}/`);
51
+ let pathname = hasBase ? targetUrl.pathname.slice(base.length) || '/' : targetUrl.pathname;
52
+ const [, firstSegment = '', remainder = ''] = pathname.match(/^\/([^/]*)(.*)$/u) ?? [];
53
+ const isHtmlRoute = firstSegment.endsWith('.html');
54
+ const currentSlug = isHtmlRoute ? firstSegment.slice(0, -5) : firstSegment;
55
+ const currentLocale = configuredLocales.find((locale) => locale !== 'root' && locale === currentSlug);
56
+ if (currentLocale) {
57
+ if (targetLocale === 'root') {
58
+ pathname = isHtmlRoute ? '/index.html' : remainder || '/';
59
+ }
60
+ else {
61
+ pathname = `/${targetLocale}${isHtmlRoute ? '.html' : remainder}`;
62
+ }
63
+ }
64
+ else if (targetLocale !== 'root') {
65
+ pathname = firstSegment === 'index.html'
66
+ ? `/${targetLocale}.html`
67
+ : `/${targetLocale}${pathname}`;
68
+ }
69
+ targetUrl.pathname = `${hasBase ? base : ''}${pathname}` || '/';
70
+ if (trailingSlash === 'never') {
71
+ targetUrl.pathname = targetUrl.pathname.replace(/\/+$/u, '') || '/';
72
+ }
73
+ return targetUrl;
74
+ }
75
+ export function persistStarlightLocaleSelection(locale) {
76
+ try {
77
+ const storedValue = window.localStorage.getItem('starlight-route');
78
+ let routePreference = {};
79
+ if (storedValue) {
80
+ try {
81
+ const parsed = JSON.parse(storedValue);
82
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
83
+ routePreference = parsed;
84
+ }
85
+ }
86
+ catch {
87
+ routePreference = {};
88
+ }
89
+ }
90
+ window.localStorage.setItem('starlight-route', JSON.stringify({ ...routePreference, lang: locale }));
91
+ }
92
+ catch {
93
+ // Language navigation must remain available when browser storage is blocked.
94
+ }
95
+ }
@@ -0,0 +1,48 @@
1
+ export interface LocaleDefinition {
2
+ label: string;
3
+ lang: string;
4
+ }
5
+ /** Starlight `locales` map for every language HagiCode documentation publishes. */
6
+ export declare const locales: {
7
+ readonly root: {
8
+ readonly label: "English";
9
+ readonly lang: "en-US";
10
+ };
11
+ readonly 'zh-CN': {
12
+ readonly label: "简体中文";
13
+ readonly lang: "zh-CN";
14
+ };
15
+ readonly 'zh-Hant': {
16
+ readonly label: "繁體中文";
17
+ readonly lang: "zh-Hant";
18
+ };
19
+ readonly 'fr-FR': {
20
+ readonly label: "Français";
21
+ readonly lang: "fr-FR";
22
+ };
23
+ readonly 'de-DE': {
24
+ readonly label: "Deutsch";
25
+ readonly lang: "de-DE";
26
+ };
27
+ readonly 'es-ES': {
28
+ readonly label: "Español (España)";
29
+ readonly lang: "es-ES";
30
+ };
31
+ readonly 'ja-JP': {
32
+ readonly label: "日本語";
33
+ readonly lang: "ja-JP";
34
+ };
35
+ readonly 'ko-KR': {
36
+ readonly label: "한국어";
37
+ readonly lang: "ko-KR";
38
+ };
39
+ readonly 'pt-BR': {
40
+ readonly label: "Português (Brasil)";
41
+ readonly lang: "pt-BR";
42
+ };
43
+ readonly 'ru-RU': {
44
+ readonly label: "Русский";
45
+ readonly lang: "ru-RU";
46
+ };
47
+ };
48
+ export type HagicodeLocaleKey = keyof typeof locales;
@@ -0,0 +1,13 @@
1
+ /** Starlight `locales` map for every language HagiCode documentation publishes. */
2
+ export const locales = {
3
+ root: { label: 'English', lang: 'en-US' },
4
+ 'zh-CN': { label: '简体中文', lang: 'zh-CN' },
5
+ 'zh-Hant': { label: '繁體中文', lang: 'zh-Hant' },
6
+ 'fr-FR': { label: 'Français', lang: 'fr-FR' },
7
+ 'de-DE': { label: 'Deutsch', lang: 'de-DE' },
8
+ 'es-ES': { label: 'Español (España)', lang: 'es-ES' },
9
+ 'ja-JP': { label: '日本語', lang: 'ja-JP' },
10
+ 'ko-KR': { label: '한국어', lang: 'ko-KR' },
11
+ 'pt-BR': { label: 'Português (Brasil)', lang: 'pt-BR' },
12
+ 'ru-RU': { label: 'Русский', lang: 'ru-RU' },
13
+ };
@@ -0,0 +1,9 @@
1
+ export type TrailingSlash = 'always' | 'never' | 'ignore';
2
+ export declare function isNotFoundEntry(entryId: string): boolean;
3
+ export interface NotFoundHomeHrefOptions {
4
+ basePath?: string;
5
+ locale?: string | undefined;
6
+ trailingSlash?: TrailingSlash;
7
+ }
8
+ export declare function getNotFoundHomeHref({ basePath, locale, trailingSlash }: NotFoundHomeHrefOptions): string;
9
+ export declare function getNotFoundHomeLabel(lang: string | undefined): string;
@@ -0,0 +1,28 @@
1
+ const homeLabels = {
2
+ de: 'Zur Startseite',
3
+ en: 'Back to home',
4
+ es: 'Volver al inicio',
5
+ fr: 'Retour à l’accueil',
6
+ ja: 'ホームに戻る',
7
+ ko: '홈으로 돌아가기',
8
+ pt: 'Voltar ao início',
9
+ ru: 'На главную',
10
+ 'zh-cn': '返回首页',
11
+ 'zh-hant': '返回首頁',
12
+ };
13
+ export function isNotFoundEntry(entryId) {
14
+ return entryId === '404' || entryId.endsWith('/404');
15
+ }
16
+ export function getNotFoundHomeHref({ basePath = '/', locale, trailingSlash = 'ignore' }) {
17
+ const base = basePath.replace(/\/+$/u, '');
18
+ const localePath = locale && locale !== 'root' ? `/${locale}` : '';
19
+ const path = `${base}${localePath}` || '/';
20
+ if (trailingSlash === 'never') {
21
+ return path.replace(/\/+$/u, '') || '/';
22
+ }
23
+ return `${path.replace(/\/+$/u, '')}/`;
24
+ }
25
+ export function getNotFoundHomeLabel(lang) {
26
+ const language = lang?.replaceAll('_', '-').toLowerCase() ?? 'en';
27
+ return homeLabels[language] ?? homeLabels[language.split('-')[0]] ?? homeLabels.en;
28
+ }
@@ -0,0 +1,14 @@
1
+ import { type RssLocale } from '@hagicode/hagilight-core/rss';
2
+ import { type ResolvedRssContentOptions, type RssDocsEntry } from './rss-utils.js';
3
+ /** Serialized configuration exposed to the Starlight RSS routes. */
4
+ export interface StarlightRssRuntimeConfig {
5
+ options: ResolvedRssContentOptions;
6
+ locales: readonly RssLocale[];
7
+ }
8
+ export interface StarlightRssFeedInput extends StarlightRssRuntimeConfig {
9
+ site: string | URL | undefined;
10
+ baseUrl: string;
11
+ filename: string | undefined;
12
+ entries: readonly RssDocsEntry[];
13
+ }
14
+ export declare function generateRssFeed({ site, baseUrl, filename, locales, options, entries, }: StarlightRssFeedInput): Promise<Response>;
@@ -0,0 +1,30 @@
1
+ import { generateRssFeed as generateCoreRssFeed } from '@hagicode/hagilight-core/rss';
2
+ import { selectRssEntries } from './rss-utils.js';
3
+ export function generateRssFeed({ site, baseUrl, filename, locales, options, entries, }) {
4
+ if (!site)
5
+ throw new Error('Hagilight RSS requires the Astro site option.');
6
+ const locale = locales.find((item) => item.filename === filename);
7
+ if (!locale && filename !== 'en') {
8
+ throw new Error(`Hagilight RSS has no configured locale for "${filename}".`);
9
+ }
10
+ const items = selectRssEntries(entries, { filename, locales, options }).map(({ id, data }) => {
11
+ const slug = id.replace(/(?:^|\/)index$/u, '');
12
+ const lastUpdated = data.lastUpdated;
13
+ return {
14
+ title: data.title,
15
+ description: data.description,
16
+ ...(lastUpdated instanceof Date && !Number.isNaN(lastUpdated.getTime())
17
+ ? { date: lastUpdated }
18
+ : {}),
19
+ link: slug ? `${slug}/` : '/',
20
+ };
21
+ });
22
+ return generateCoreRssFeed({
23
+ site,
24
+ baseUrl,
25
+ language: locale?.lang ?? 'en',
26
+ title: 'Documentation',
27
+ description: 'Recently updated documentation pages',
28
+ items,
29
+ });
30
+ }
@@ -0,0 +1,27 @@
1
+ import type { RssLocale } from '@hagicode/hagilight-core/rss';
2
+ export interface RssContentOptions {
3
+ /** Include non-blog documentation pages. Defaults to `true`. */
4
+ includeDocs?: boolean;
5
+ /** Include pages under `blog/`. Defaults to `true`. */
6
+ includeBlog?: boolean;
7
+ }
8
+ export type ResolvedRssContentOptions = Required<RssContentOptions>;
9
+ /** The subset of a Starlight docs collection entry used to build feeds. */
10
+ export interface RssDocsEntry {
11
+ id: string;
12
+ data: {
13
+ title: string;
14
+ description?: string | undefined;
15
+ draft?: boolean | undefined;
16
+ rss?: unknown;
17
+ lastUpdated?: unknown;
18
+ };
19
+ }
20
+ export interface SelectRssEntriesOptions {
21
+ filename: string | undefined;
22
+ locales: readonly RssLocale[];
23
+ options: ResolvedRssContentOptions;
24
+ }
25
+ export declare function resolveRssOptions(options?: RssContentOptions): ResolvedRssContentOptions;
26
+ export declare function isBlogEntry(id: string): boolean;
27
+ export declare function selectRssEntries<Entry extends RssDocsEntry>(entries: readonly Entry[], { filename, locales, options }: SelectRssEntriesOptions): Entry[];
@@ -0,0 +1,65 @@
1
+ export function resolveRssOptions(options = {}) {
2
+ if (!options || typeof options !== 'object' || Array.isArray(options)) {
3
+ throw new TypeError('Hagilight rss options must be an object.');
4
+ }
5
+ for (const key of ['includeDocs', 'includeBlog']) {
6
+ if (options[key] !== undefined && typeof options[key] !== 'boolean') {
7
+ throw new TypeError(`Hagilight rss ${key} option must be a boolean.`);
8
+ }
9
+ }
10
+ return {
11
+ includeDocs: options.includeDocs ?? true,
12
+ includeBlog: options.includeBlog ?? true,
13
+ };
14
+ }
15
+ function getLocaleForEntry(id, locales) {
16
+ const localized = locales
17
+ .filter(({ route }) => route !== 'root')
18
+ .sort((a, b) => b.route.length - a.route.length)
19
+ .find(({ route }) => id === route || id.startsWith(`${route}/`));
20
+ if (localized)
21
+ return { locale: localized, relativeId: id.slice(localized.route.length).replace(/^\/+/, '') };
22
+ const root = locales.find(({ route }) => route === 'root');
23
+ if (root)
24
+ return { locale: root, relativeId: id };
25
+ return undefined;
26
+ }
27
+ export function isBlogEntry(id) {
28
+ const segments = id.split('/').filter(Boolean);
29
+ if (segments.at(-1) === 'index')
30
+ segments.pop();
31
+ return segments[0] === 'blog' && segments.length > 1;
32
+ }
33
+ function timestamp(value) {
34
+ return value instanceof Date ? value.getTime() : undefined;
35
+ }
36
+ export function selectRssEntries(entries, { filename, locales, options }) {
37
+ const locale = locales.find((item) => item.filename === filename);
38
+ if (!locale) {
39
+ if (filename === 'en')
40
+ return [];
41
+ throw new Error(`Hagilight RSS has no configured locale for "${filename}".`);
42
+ }
43
+ return entries
44
+ .filter(({ id, data }) => {
45
+ if (data.rss !== undefined && typeof data.rss !== 'boolean') {
46
+ throw new TypeError(`Hagilight RSS frontmatter field "rss" for "${id}" must be a boolean.`);
47
+ }
48
+ if (data.draft || data.rss === false)
49
+ return false;
50
+ const entryLocale = getLocaleForEntry(id, locales);
51
+ if (!entryLocale || entryLocale.locale.filename !== filename)
52
+ return false;
53
+ const isBlog = isBlogEntry(entryLocale.relativeId);
54
+ return isBlog ? options.includeBlog : options.includeDocs;
55
+ })
56
+ .sort((a, b) => {
57
+ const aDate = timestamp(a.data.lastUpdated);
58
+ const bDate = timestamp(b.data.lastUpdated);
59
+ if (aDate === undefined || Number.isNaN(aDate))
60
+ return bDate === undefined || Number.isNaN(bDate) ? 0 : 1;
61
+ if (bDate === undefined || Number.isNaN(bDate))
62
+ return -1;
63
+ return bDate - aDate;
64
+ });
65
+ }
@@ -0,0 +1,32 @@
1
+ import { z } from 'astro/zod';
2
+ /** Per-page AI disclosure flags that override the plugin `aiDisclosures` defaults. */
3
+ export declare const aiDisclosureSchema: z.ZodObject<{
4
+ isAITranslation: z.ZodOptional<z.ZodBoolean>;
5
+ isAIAuthor: z.ZodOptional<z.ZodBoolean>;
6
+ }, z.core.$strip>;
7
+ /** Per-page switch for the end-of-article HagiCode introduction. */
8
+ export declare const articlePromotionSchema: z.ZodObject<{
9
+ hagicodePromotion: z.ZodOptional<z.ZodBoolean>;
10
+ }, z.core.$strip>;
11
+ /** Per-page switch that excludes a page from generated RSS feeds when `false`. */
12
+ export declare const rssSchema: z.ZodObject<{
13
+ rss: z.ZodOptional<z.ZodBoolean>;
14
+ }, z.core.$strip>;
15
+ /**
16
+ * Every frontmatter field read by the Hagilight Starlight plugin, including the
17
+ * shared `seo` object. Pass it to Starlight as `docsSchema({ extend: hagilightSchema })`.
18
+ */
19
+ export declare const hagilightSchema: z.ZodObject<{
20
+ seo: z.ZodOptional<z.ZodObject<{
21
+ title: z.ZodOptional<z.ZodString>;
22
+ description: z.ZodOptional<z.ZodString>;
23
+ image: z.ZodOptional<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>;
24
+ author: z.ZodOptional<z.ZodString>;
25
+ publishedDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
26
+ }, z.core.$strip>>;
27
+ rss: z.ZodOptional<z.ZodBoolean>;
28
+ hagicodePromotion: z.ZodOptional<z.ZodBoolean>;
29
+ isAITranslation: z.ZodOptional<z.ZodBoolean>;
30
+ isAIAuthor: z.ZodOptional<z.ZodBoolean>;
31
+ }, z.core.$strip>;
32
+ export type HagilightFrontmatter = z.infer<typeof hagilightSchema>;
package/dist/schema.js ADDED
@@ -0,0 +1,25 @@
1
+ import { z } from 'astro/zod';
2
+ import { seoSchema } from '@hagicode/hagilight-core/seo-schema';
3
+ /** Per-page AI disclosure flags that override the plugin `aiDisclosures` defaults. */
4
+ export const aiDisclosureSchema = z.object({
5
+ isAITranslation: z.boolean().optional(),
6
+ isAIAuthor: z.boolean().optional(),
7
+ });
8
+ /** Per-page switch for the end-of-article HagiCode introduction. */
9
+ export const articlePromotionSchema = z.object({
10
+ hagicodePromotion: z.boolean().optional(),
11
+ });
12
+ /** Per-page switch that excludes a page from generated RSS feeds when `false`. */
13
+ export const rssSchema = z.object({
14
+ rss: z.boolean().optional(),
15
+ });
16
+ /**
17
+ * Every frontmatter field read by the Hagilight Starlight plugin, including the
18
+ * shared `seo` object. Pass it to Starlight as `docsSchema({ extend: hagilightSchema })`.
19
+ */
20
+ export const hagilightSchema = z.object({
21
+ ...aiDisclosureSchema.shape,
22
+ ...articlePromotionSchema.shape,
23
+ ...rssSchema.shape,
24
+ ...seoSchema.shape,
25
+ });
@@ -0,0 +1,74 @@
1
+ import { type HeadEntry, type StructuredData } from '@hagicode/hagilight-core/seo';
2
+ import type { TrailingSlash } from './not-found.js';
3
+ export interface SeoLocale {
4
+ route: string;
5
+ lang: string;
6
+ }
7
+ export interface SeoPage {
8
+ route: string;
9
+ slug: string;
10
+ }
11
+ export interface SeoDocsEntry {
12
+ id: string;
13
+ body?: string | undefined;
14
+ data: {
15
+ title: string;
16
+ description?: string | undefined;
17
+ draft?: boolean | undefined;
18
+ author?: string | undefined;
19
+ publishedDate?: Date | string | number | undefined;
20
+ lastUpdated?: Date | string | number | undefined | unknown;
21
+ [key: string]: unknown;
22
+ };
23
+ }
24
+ export interface PublishedDocsLookup<Entry extends SeoDocsEntry = SeoDocsEntry> {
25
+ get(route: string, slug: string): Entry | undefined;
26
+ has(route: string, slug: string): boolean;
27
+ }
28
+ export type BuildFormat = 'directory' | 'file' | 'preserve';
29
+ export interface SeoOrganization {
30
+ name: string;
31
+ url: string;
32
+ logo?: string;
33
+ }
34
+ export type SeoLocalesInput = Readonly<Record<string, string | {
35
+ lang?: string | undefined;
36
+ } | undefined>>;
37
+ export declare function resolveSeoLocales(locales: SeoLocalesInput | undefined): SeoLocale[];
38
+ export declare function resolveSeoPage(entryId: string | undefined, locales: readonly SeoLocale[]): SeoPage;
39
+ export declare function createPublishedDocsLookup<Entry extends SeoDocsEntry>(entries: readonly Entry[], locales: readonly SeoLocale[]): PublishedDocsLookup<Entry>;
40
+ export interface FilterAlternatesOptions {
41
+ entryId: string;
42
+ locales: readonly SeoLocale[];
43
+ lookup: PublishedDocsLookup<SeoDocsEntry>;
44
+ basePath?: string;
45
+ explicitHead?: readonly (readonly HeadEntry[] | undefined)[];
46
+ }
47
+ export declare function filterPublishedLocaleAlternates<Entry extends HeadEntry>(head: readonly Entry[], { entryId, locales, lookup, basePath, explicitHead, }: FilterAlternatesOptions): Entry[];
48
+ export interface DocsPageUrlOptions {
49
+ site: string | URL;
50
+ basePath?: string;
51
+ format?: BuildFormat;
52
+ trailingSlash?: TrailingSlash;
53
+ }
54
+ export declare function buildDocsPageUrl(route: string, slug: string, { site, basePath, format, trailingSlash, }: DocsPageUrlOptions): string;
55
+ export interface BuildStructuredDataOptions {
56
+ entry: SeoDocsEntry | undefined;
57
+ entryId: string;
58
+ canonicalUrl: string | undefined;
59
+ site: string | URL;
60
+ siteTitle?: string | undefined;
61
+ pageSeo?: {
62
+ description?: string | undefined;
63
+ author?: string | undefined;
64
+ publishedDate?: Date | string | number | undefined;
65
+ };
66
+ organization?: SeoOrganization | undefined;
67
+ locales: readonly SeoLocale[];
68
+ defaultLocale?: string;
69
+ lookup: PublishedDocsLookup<SeoDocsEntry>;
70
+ basePath?: string;
71
+ format?: BuildFormat;
72
+ trailingSlash?: TrailingSlash;
73
+ }
74
+ export declare function buildStructuredData({ entry, entryId, canonicalUrl, site, siteTitle, pageSeo, organization, locales, defaultLocale, lookup, basePath, format, trailingSlash, }: BuildStructuredDataOptions): StructuredData[];