svedocs 0.1.0-beta.10
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 +5 -0
- package/README.md +85 -0
- package/dist/ai/openai-compatible.d.ts +13 -0
- package/dist/ai/openai-compatible.d.ts.map +1 -0
- package/dist/ai/openai-compatible.js +92 -0
- package/dist/ai/providers.d.ts +20 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +185 -0
- package/dist/ai/rate-limit.d.ts +18 -0
- package/dist/ai/rate-limit.d.ts.map +1 -0
- package/dist/ai/rate-limit.js +52 -0
- package/dist/ai/response.d.ts +4 -0
- package/dist/ai/response.d.ts.map +1 -0
- package/dist/ai/response.js +151 -0
- package/dist/ai/runtime.d.ts +32 -0
- package/dist/ai/runtime.d.ts.map +1 -0
- package/dist/ai/runtime.js +111 -0
- package/dist/ai/types.d.ts +93 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +1 -0
- package/dist/ai.d.ts +7 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +6 -0
- package/dist/ambient.d.ts +5 -0
- package/dist/cloudflare.d.ts +29 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +149 -0
- package/dist/config.d.ts +373 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +299 -0
- package/dist/core/checks.d.ts +4 -0
- package/dist/core/checks.d.ts.map +1 -0
- package/dist/core/checks.js +267 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +178 -0
- package/dist/core/content.d.ts +7 -0
- package/dist/core/content.d.ts.map +1 -0
- package/dist/core/content.js +340 -0
- package/dist/core/i18n.d.ts +5 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/i18n.js +117 -0
- package/dist/core/links.d.ts +5 -0
- package/dist/core/links.d.ts.map +1 -0
- package/dist/core/links.js +60 -0
- package/dist/core/messages.d.ts +149 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +163 -0
- package/dist/core/navigation.d.ts +5 -0
- package/dist/core/navigation.d.ts.map +1 -0
- package/dist/core/navigation.js +144 -0
- package/dist/core/routes.d.ts +36 -0
- package/dist/core/routes.d.ts.map +1 -0
- package/dist/core/routes.js +152 -0
- package/dist/core/search.d.ts +5 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +41 -0
- package/dist/core/types.d.ts +468 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/utils.d.ts +13 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +60 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +8 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/mdx/ast.d.ts +32 -0
- package/dist/mdx/ast.d.ts.map +1 -0
- package/dist/mdx/ast.js +148 -0
- package/dist/mdx/code.d.ts +17 -0
- package/dist/mdx/code.d.ts.map +1 -0
- package/dist/mdx/code.js +327 -0
- package/dist/mdx/compile.d.ts +29 -0
- package/dist/mdx/compile.d.ts.map +1 -0
- package/dist/mdx/compile.js +59 -0
- package/dist/mdx/diff.d.ts +9 -0
- package/dist/mdx/diff.d.ts.map +1 -0
- package/dist/mdx/diff.js +171 -0
- package/dist/mdx/headings.d.ts +4 -0
- package/dist/mdx/headings.d.ts.map +1 -0
- package/dist/mdx/headings.js +42 -0
- package/dist/mdx/links.d.ts +4 -0
- package/dist/mdx/links.d.ts.map +1 -0
- package/dist/mdx/links.js +144 -0
- package/dist/mdx/utils.d.ts +10 -0
- package/dist/mdx/utils.d.ts.map +1 -0
- package/dist/mdx/utils.js +64 -0
- package/dist/og/image.d.ts +25 -0
- package/dist/og/image.d.ts.map +1 -0
- package/dist/og/image.js +224 -0
- package/dist/og/metadata.d.ts +7 -0
- package/dist/og/metadata.d.ts.map +1 -0
- package/dist/og/metadata.js +198 -0
- package/dist/og/response.d.ts +4 -0
- package/dist/og/response.d.ts.map +1 -0
- package/dist/og/response.js +49 -0
- package/dist/og/rss.d.ts +4 -0
- package/dist/og/rss.d.ts.map +1 -0
- package/dist/og/rss.js +96 -0
- package/dist/og/sitemap.d.ts +7 -0
- package/dist/og/sitemap.d.ts.map +1 -0
- package/dist/og/sitemap.js +90 -0
- package/dist/og/types.d.ts +65 -0
- package/dist/og/types.d.ts.map +1 -0
- package/dist/og/types.js +1 -0
- package/dist/og.d.ts +6 -0
- package/dist/og.d.ts.map +1 -0
- package/dist/og.js +5 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +1 -0
- package/dist/search/algolia.d.ts +10 -0
- package/dist/search/algolia.d.ts.map +1 -0
- package/dist/search/algolia.js +82 -0
- package/dist/search/cloudflare-sync.d.ts +5 -0
- package/dist/search/cloudflare-sync.d.ts.map +1 -0
- package/dist/search/cloudflare-sync.js +169 -0
- package/dist/search/cloudflare.d.ts +11 -0
- package/dist/search/cloudflare.d.ts.map +1 -0
- package/dist/search/cloudflare.js +126 -0
- package/dist/search/local.d.ts +9 -0
- package/dist/search/local.d.ts.map +1 -0
- package/dist/search/local.js +165 -0
- package/dist/search/runtime.d.ts +41 -0
- package/dist/search/runtime.d.ts.map +1 -0
- package/dist/search/runtime.js +152 -0
- package/dist/search/types.d.ts +172 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +1 -0
- package/dist/search/typesense.d.ts +11 -0
- package/dist/search/typesense.d.ts.map +1 -0
- package/dist/search/typesense.js +91 -0
- package/dist/search/utils.d.ts +11 -0
- package/dist/search/utils.d.ts.map +1 -0
- package/dist/search/utils.js +93 -0
- package/dist/search.d.ts +8 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +7 -0
- package/dist/svelte.d.ts +27 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +45 -0
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +23 -0
- package/dist/theme/Article.svelte +145 -0
- package/dist/theme/Article.svelte.d.ts +36 -0
- package/dist/theme/Article.svelte.d.ts.map +1 -0
- package/dist/theme/AskAiPanel.svelte +241 -0
- package/dist/theme/AskAiPanel.svelte.d.ts +31 -0
- package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
- package/dist/theme/Brand.svelte +19 -0
- package/dist/theme/Brand.svelte.d.ts +22 -0
- package/dist/theme/Brand.svelte.d.ts.map +1 -0
- package/dist/theme/Button.svelte +34 -0
- package/dist/theme/Button.svelte.d.ts +41 -0
- package/dist/theme/Button.svelte.d.ts.map +1 -0
- package/dist/theme/Checkbox.svelte +63 -0
- package/dist/theme/Checkbox.svelte.d.ts +45 -0
- package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
- package/dist/theme/CodeBlock.svelte +6 -0
- package/dist/theme/CodeBlock.svelte.d.ts +22 -0
- package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
- package/dist/theme/DocPage.svelte +15 -0
- package/dist/theme/DocPage.svelte.d.ts +33 -0
- package/dist/theme/DocPage.svelte.d.ts.map +1 -0
- package/dist/theme/DocsApp.svelte +194 -0
- package/dist/theme/DocsApp.svelte.d.ts +60 -0
- package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
- package/dist/theme/DocsLayout.svelte +99 -0
- package/dist/theme/DocsLayout.svelte.d.ts +39 -0
- package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
- package/dist/theme/DocsShell.svelte +91 -0
- package/dist/theme/DocsShell.svelte.d.ts +34 -0
- package/dist/theme/DocsShell.svelte.d.ts.map +1 -0
- package/dist/theme/ErrorPage.svelte +113 -0
- package/dist/theme/ErrorPage.svelte.d.ts +34 -0
- package/dist/theme/ErrorPage.svelte.d.ts.map +1 -0
- package/dist/theme/FloatingToolbar.svelte +8 -0
- package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
- package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
- package/dist/theme/Footer.svelte +18 -0
- package/dist/theme/Footer.svelte.d.ts +24 -0
- package/dist/theme/Footer.svelte.d.ts.map +1 -0
- package/dist/theme/FooterLinks.svelte +37 -0
- package/dist/theme/FooterLinks.svelte.d.ts +22 -0
- package/dist/theme/FooterLinks.svelte.d.ts.map +1 -0
- package/dist/theme/FormField.svelte +41 -0
- package/dist/theme/FormField.svelte.d.ts +35 -0
- package/dist/theme/FormField.svelte.d.ts.map +1 -0
- package/dist/theme/HomePage.svelte +284 -0
- package/dist/theme/HomePage.svelte.d.ts +65 -0
- package/dist/theme/HomePage.svelte.d.ts.map +1 -0
- package/dist/theme/Input.svelte +36 -0
- package/dist/theme/Input.svelte.d.ts +34 -0
- package/dist/theme/Input.svelte.d.ts.map +1 -0
- package/dist/theme/LayoutShell.svelte +120 -0
- package/dist/theme/LayoutShell.svelte.d.ts +43 -0
- package/dist/theme/LayoutShell.svelte.d.ts.map +1 -0
- package/dist/theme/LocalizedLink.svelte +21 -0
- package/dist/theme/LocalizedLink.svelte.d.ts +38 -0
- package/dist/theme/LocalizedLink.svelte.d.ts.map +1 -0
- package/dist/theme/MobileNav.svelte +20 -0
- package/dist/theme/MobileNav.svelte.d.ts +26 -0
- package/dist/theme/MobileNav.svelte.d.ts.map +1 -0
- package/dist/theme/Navbar.svelte +69 -0
- package/dist/theme/Navbar.svelte.d.ts +29 -0
- package/dist/theme/Navbar.svelte.d.ts.map +1 -0
- package/dist/theme/PageLayout.svelte +49 -0
- package/dist/theme/PageLayout.svelte.d.ts +34 -0
- package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
- package/dist/theme/PageShell.svelte +98 -0
- package/dist/theme/PageShell.svelte.d.ts +35 -0
- package/dist/theme/PageShell.svelte.d.ts.map +1 -0
- package/dist/theme/PageTools.svelte +87 -0
- package/dist/theme/PageTools.svelte.d.ts +25 -0
- package/dist/theme/PageTools.svelte.d.ts.map +1 -0
- package/dist/theme/RenderError.svelte +62 -0
- package/dist/theme/RenderError.svelte.d.ts +31 -0
- package/dist/theme/RenderError.svelte.d.ts.map +1 -0
- package/dist/theme/RootLayout.svelte +274 -0
- package/dist/theme/RootLayout.svelte.d.ts +47 -0
- package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
- package/dist/theme/RouteRenderError.svelte +53 -0
- package/dist/theme/RouteRenderError.svelte.d.ts +34 -0
- package/dist/theme/RouteRenderError.svelte.d.ts.map +1 -0
- package/dist/theme/SafeRenderError.svelte +49 -0
- package/dist/theme/SafeRenderError.svelte.d.ts +33 -0
- package/dist/theme/SafeRenderError.svelte.d.ts.map +1 -0
- package/dist/theme/ScopeSwitcher.svelte +107 -0
- package/dist/theme/ScopeSwitcher.svelte.d.ts +26 -0
- package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
- package/dist/theme/SearchDialog.svelte +200 -0
- package/dist/theme/SearchDialog.svelte.d.ts +31 -0
- package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
- package/dist/theme/Select.svelte +37 -0
- package/dist/theme/Select.svelte.d.ts +42 -0
- package/dist/theme/Select.svelte.d.ts.map +1 -0
- package/dist/theme/SidebarTree.svelte +148 -0
- package/dist/theme/SidebarTree.svelte.d.ts +24 -0
- package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
- package/dist/theme/SocialNav.svelte +17 -0
- package/dist/theme/SocialNav.svelte.d.ts +22 -0
- package/dist/theme/SocialNav.svelte.d.ts.map +1 -0
- package/dist/theme/TableOfContents.svelte +73 -0
- package/dist/theme/TableOfContents.svelte.d.ts +25 -0
- package/dist/theme/TableOfContents.svelte.d.ts.map +1 -0
- package/dist/theme/Textarea.svelte +33 -0
- package/dist/theme/Textarea.svelte.d.ts +33 -0
- package/dist/theme/Textarea.svelte.d.ts.map +1 -0
- package/dist/theme/ThemeInit.svelte +16 -0
- package/dist/theme/ThemeInit.svelte.d.ts +23 -0
- package/dist/theme/ThemeInit.svelte.d.ts.map +1 -0
- package/dist/theme/ThemeToggle.svelte +37 -0
- package/dist/theme/ThemeToggle.svelte.d.ts +23 -0
- package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
- package/dist/theme/TopNav.svelte +23 -0
- package/dist/theme/TopNav.svelte.d.ts +22 -0
- package/dist/theme/TopNav.svelte.d.ts.map +1 -0
- package/dist/theme/base.css +240 -0
- package/dist/theme/defaults.d.ts +4 -0
- package/dist/theme/defaults.d.ts.map +1 -0
- package/dist/theme/defaults.js +55 -0
- package/dist/theme/forms.d.ts +4 -0
- package/dist/theme/forms.d.ts.map +1 -0
- package/dist/theme/forms.js +3 -0
- package/dist/theme/headless.d.ts +62 -0
- package/dist/theme/headless.d.ts.map +1 -0
- package/dist/theme/headless.js +1064 -0
- package/dist/theme/index.d.ts +41 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +38 -0
- package/dist/theme/portal.d.ts +6 -0
- package/dist/theme/portal.d.ts.map +1 -0
- package/dist/theme/portal.js +59 -0
- package/dist/theme/styles.css +4232 -0
- package/dist/theme/types.d.ts +363 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/types.js +1 -0
- package/dist/virtual.d.ts +65 -0
- package/dist/vite.d.ts +16 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +386 -0
- package/package.json +132 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { formatRoutePathForBuildMode } from '../core/utils.js';
|
|
2
|
+
import { createPageAlternates } from './metadata.js';
|
|
3
|
+
import { createDisabledDiscoveryResponse, createDiscoveryResponse, escapeXml } from './response.js';
|
|
4
|
+
export function createSitemapXml(config, pages) {
|
|
5
|
+
if (!config.seo.sitemap)
|
|
6
|
+
return '';
|
|
7
|
+
const discoverablePages = pages.filter(isDiscoverablePage);
|
|
8
|
+
const alternateIndex = createAlternateIndex(config, discoverablePages);
|
|
9
|
+
const urls = discoverablePages
|
|
10
|
+
.map((page) => {
|
|
11
|
+
const loc = createSitemapLocation(config, page);
|
|
12
|
+
if (!loc)
|
|
13
|
+
return '';
|
|
14
|
+
const lastmod = page.seo.updatedTime ?? page.lastUpdated;
|
|
15
|
+
const alternates = alternateIndex.get(createAlternateKey(page)) ?? [];
|
|
16
|
+
return [
|
|
17
|
+
' <url>',
|
|
18
|
+
` <loc>${escapeXml(loc)}</loc>`,
|
|
19
|
+
...(lastmod ? [` <lastmod>${escapeXml(lastmod)}</lastmod>`] : []),
|
|
20
|
+
...alternates.map((alternate) => ` <xhtml:link rel="alternate" hreflang="${escapeXml(alternate.lang)}" href="${escapeXml(alternate.href)}" />`),
|
|
21
|
+
' </url>'
|
|
22
|
+
].join('\n');
|
|
23
|
+
})
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.join('\n');
|
|
26
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">\n${urls}\n</urlset>\n`;
|
|
27
|
+
}
|
|
28
|
+
export function createRobotsTxt(config) {
|
|
29
|
+
if (!config.seo.robots)
|
|
30
|
+
return '';
|
|
31
|
+
const sitemap = config.seo.sitemap ? createAbsoluteUrl(config, '/sitemap.xml') : undefined;
|
|
32
|
+
return [
|
|
33
|
+
'User-agent: *',
|
|
34
|
+
'Allow: /',
|
|
35
|
+
...(sitemap ? [`Sitemap: ${sitemap}`] : [])
|
|
36
|
+
].join('\n') + '\n';
|
|
37
|
+
}
|
|
38
|
+
export function createSitemapResponse(config, pages, request) {
|
|
39
|
+
if (!config.seo.sitemap)
|
|
40
|
+
return createDisabledDiscoveryResponse('Sitemap');
|
|
41
|
+
return createDiscoveryResponse(createSitemapXml(config, pages), 'application/xml; charset=utf-8', request);
|
|
42
|
+
}
|
|
43
|
+
export function createRobotsResponse(config, request) {
|
|
44
|
+
if (!config.seo.robots)
|
|
45
|
+
return createDisabledDiscoveryResponse('Robots');
|
|
46
|
+
return createDiscoveryResponse(createRobotsTxt(config), 'text/plain; charset=utf-8', request);
|
|
47
|
+
}
|
|
48
|
+
export function isDiscoverablePage(page) {
|
|
49
|
+
return !page.hidden && !/(?:^|[\s,])(?:noindex|none)(?:$|[\s,])/i.test(page.seo.robots ?? '');
|
|
50
|
+
}
|
|
51
|
+
function createAlternateIndex(config, pages) {
|
|
52
|
+
const groups = new Map();
|
|
53
|
+
for (const page of pages) {
|
|
54
|
+
const key = createAlternateKey(page);
|
|
55
|
+
const group = groups.get(key) ?? [];
|
|
56
|
+
group.push(page);
|
|
57
|
+
groups.set(key, group);
|
|
58
|
+
}
|
|
59
|
+
const alternates = new Map();
|
|
60
|
+
for (const [key, group] of groups) {
|
|
61
|
+
const reference = group[0];
|
|
62
|
+
if (reference)
|
|
63
|
+
alternates.set(key, createPageAlternates(config, reference, group));
|
|
64
|
+
}
|
|
65
|
+
return alternates;
|
|
66
|
+
}
|
|
67
|
+
function createAlternateKey(page) {
|
|
68
|
+
return `${page.kind}:${page.scopePath}`;
|
|
69
|
+
}
|
|
70
|
+
function createSitemapLocation(config, page) {
|
|
71
|
+
const routePath = formatRoutePathForBuildMode(page.routePath, config.build.mode);
|
|
72
|
+
const location = page.seo.canonical
|
|
73
|
+
? createAbsoluteUrl(config, page.seo.canonical) ?? page.seo.canonical
|
|
74
|
+
: createAbsoluteUrl(config, routePath) ?? routePath;
|
|
75
|
+
if (!config.site.url || !/^https?:\/\//.test(location))
|
|
76
|
+
return location;
|
|
77
|
+
try {
|
|
78
|
+
return new URL(location).origin === new URL(config.site.url).origin ? location : undefined;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function createAbsoluteUrl(config, value) {
|
|
85
|
+
if (/^https?:\/\//.test(value))
|
|
86
|
+
return value;
|
|
87
|
+
if (!config.site.url)
|
|
88
|
+
return undefined;
|
|
89
|
+
return new URL(value, config.site.url).href;
|
|
90
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { SvedocsResolvedSeoHead } from '../core/types.js';
|
|
2
|
+
export interface OgImageInput {
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
siteName?: string;
|
|
6
|
+
}
|
|
7
|
+
export type OgRenderer = 'svg' | 'satori';
|
|
8
|
+
export interface OgFont {
|
|
9
|
+
name: string;
|
|
10
|
+
data: ArrayBuffer | Uint8Array;
|
|
11
|
+
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
12
|
+
style?: 'normal' | 'italic';
|
|
13
|
+
}
|
|
14
|
+
export interface OgTemplateNode {
|
|
15
|
+
type: string;
|
|
16
|
+
props: {
|
|
17
|
+
style?: Record<string, string | number>;
|
|
18
|
+
children?: string | OgTemplateNode | OgTemplateNode[];
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export type OgTemplate = (input: OgImageInput) => OgTemplateNode;
|
|
23
|
+
export interface OgRenderOptions {
|
|
24
|
+
renderer?: OgRenderer;
|
|
25
|
+
fonts?: OgFont[];
|
|
26
|
+
template?: OgTemplate;
|
|
27
|
+
}
|
|
28
|
+
export interface OgImageOptions extends OgRenderOptions {
|
|
29
|
+
format?: 'svg' | 'png';
|
|
30
|
+
}
|
|
31
|
+
export interface SvedocsPageMetadata {
|
|
32
|
+
title: string;
|
|
33
|
+
description: string;
|
|
34
|
+
canonical?: string;
|
|
35
|
+
image?: string;
|
|
36
|
+
keywords: string[];
|
|
37
|
+
robots?: string;
|
|
38
|
+
head: SvedocsResolvedSeoHead;
|
|
39
|
+
openGraph: {
|
|
40
|
+
title: string;
|
|
41
|
+
description: string;
|
|
42
|
+
type: string;
|
|
43
|
+
url?: string;
|
|
44
|
+
image?: string;
|
|
45
|
+
siteName: string;
|
|
46
|
+
locale?: string;
|
|
47
|
+
alternateLocales?: string[];
|
|
48
|
+
author?: string;
|
|
49
|
+
publishedTime?: string;
|
|
50
|
+
updatedTime?: string;
|
|
51
|
+
};
|
|
52
|
+
twitter: {
|
|
53
|
+
card: 'summary' | 'summary_large_image';
|
|
54
|
+
title: string;
|
|
55
|
+
description: string;
|
|
56
|
+
image?: string;
|
|
57
|
+
};
|
|
58
|
+
jsonLd: Record<string, unknown>;
|
|
59
|
+
}
|
|
60
|
+
export interface SvedocsPageAlternate {
|
|
61
|
+
lang: string;
|
|
62
|
+
href: string;
|
|
63
|
+
locale?: string;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/og/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE1C,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IAC/B,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC7D,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;QACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;QACtD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,cAAc,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,SAAS,EAAE;QACT,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,EAAE;QACP,IAAI,EAAE,SAAS,GAAG,qBAAqB,CAAC;QACxC,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/og/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/og.d.ts
ADDED
package/dist/og.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"og.d.ts","sourceRoot":"","sources":["../src/og.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
package/dist/og.js
ADDED
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './core/routes.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SearchProvider, SearchQuery } from './types.js';
|
|
2
|
+
export interface AlgoliaSearchProviderOptions {
|
|
3
|
+
appId: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
indexName: string;
|
|
6
|
+
filters?: string | ((query: SearchQuery) => string | undefined);
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
export declare function createAlgoliaSearchProvider(options: AlgoliaSearchProviderOptions): SearchProvider;
|
|
10
|
+
//# sourceMappingURL=algolia.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"algolia.d.ts","sourceRoot":"","sources":["../../src/search/algolia.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAgB,MAAM,YAAY,CAAC;AAG5E,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IAChE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAyBD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,4BAA4B,GAAG,cAAc,CAgCjG"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { matchesSearchScope } from './local.js';
|
|
2
|
+
import { createExcerpt, stringMetadata, stringifyMetadata } from './utils.js';
|
|
3
|
+
export function createAlgoliaSearchProvider(options) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'algolia',
|
|
6
|
+
index() {
|
|
7
|
+
// Algolia indexing is usually handled by DocSearch crawlers or user CI.
|
|
8
|
+
},
|
|
9
|
+
async search(query) {
|
|
10
|
+
const requestFetch = options.fetch ?? fetch;
|
|
11
|
+
const filters = createAlgoliaFilters(options, query);
|
|
12
|
+
const response = await requestFetch(createAlgoliaQueryEndpoint(options), {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'content-type': 'application/json',
|
|
16
|
+
'x-algolia-application-id': options.appId,
|
|
17
|
+
'x-algolia-api-key': options.apiKey
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
query: query.query,
|
|
21
|
+
hitsPerPage: query.limit ?? 10,
|
|
22
|
+
...(filters ? { filters } : {}),
|
|
23
|
+
attributesToSnippet: ['content:30']
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`Algolia search returned ${response.status}: ${(await response.text()).slice(0, 500)}`);
|
|
28
|
+
}
|
|
29
|
+
const payload = await response.json();
|
|
30
|
+
return (payload.hits ?? [])
|
|
31
|
+
.map((hit, index) => createAlgoliaResult(hit, index, query.query))
|
|
32
|
+
.filter((result) => matchesSearchScope({ metadata: result.metadata }, query));
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createAlgoliaQueryEndpoint(options) {
|
|
37
|
+
return `https://${encodeURIComponent(options.appId)}-dsn.algolia.net/1/indexes/${encodeURIComponent(options.indexName)}/query`;
|
|
38
|
+
}
|
|
39
|
+
function createAlgoliaFilters(options, query) {
|
|
40
|
+
return typeof options.filters === 'function' ? options.filters(query) : options.filters;
|
|
41
|
+
}
|
|
42
|
+
function createAlgoliaResult(hit, index, query) {
|
|
43
|
+
const metadata = stringifyMetadata({
|
|
44
|
+
...(hit.metadata ?? {}),
|
|
45
|
+
locale: hit.locale ?? hit.metadata?.locale,
|
|
46
|
+
kind: hit.kind ?? hit.metadata?.kind
|
|
47
|
+
});
|
|
48
|
+
const hierarchyTitle = readHierarchy(hit.hierarchy, ['lvl0', 'lvl1', 'lvl2']);
|
|
49
|
+
const title = hit.title ?? hierarchyTitle ?? 'Result';
|
|
50
|
+
const section = hit.section ?? readHierarchy(hit.hierarchy, ['lvl6', 'lvl5', 'lvl4', 'lvl3', 'lvl2', 'lvl1']);
|
|
51
|
+
const content = stripHtml(readSnippet(hit) ?? hit.content ?? title);
|
|
52
|
+
return {
|
|
53
|
+
id: hit.objectID ?? `algolia:${index}`,
|
|
54
|
+
title,
|
|
55
|
+
url: createAlgoliaUrl(hit),
|
|
56
|
+
...(section && section !== title ? { section } : {}),
|
|
57
|
+
excerpt: createExcerpt(content, query),
|
|
58
|
+
score: 1 / (index + 1),
|
|
59
|
+
metadata
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createAlgoliaUrl(hit) {
|
|
63
|
+
const url = hit.url ?? '#';
|
|
64
|
+
if (!hit.anchor || url.includes('#'))
|
|
65
|
+
return url;
|
|
66
|
+
return `${url}#${hit.anchor}`;
|
|
67
|
+
}
|
|
68
|
+
function readSnippet(hit) {
|
|
69
|
+
return stringMetadata(hit._snippetResult?.content?.value)
|
|
70
|
+
?? stringMetadata(hit._highlightResult?.content?.value);
|
|
71
|
+
}
|
|
72
|
+
function readHierarchy(hierarchy, keys) {
|
|
73
|
+
for (const key of keys) {
|
|
74
|
+
const value = stringMetadata(hierarchy?.[key]);
|
|
75
|
+
if (value)
|
|
76
|
+
return stripHtml(value);
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
function stripHtml(value) {
|
|
81
|
+
return value.replace(/<[^>]+>/g, '').replace(/ /g, ' ').trim();
|
|
82
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SvedocsSearchRecord } from '../core.js';
|
|
2
|
+
import type { CloudflareAiSearchSyncDocument, CloudflareAiSearchSyncOptions, CloudflareAiSearchSyncResult } from './types.js';
|
|
3
|
+
export declare function createCloudflareAiSearchDocuments(records: SvedocsSearchRecord[]): CloudflareAiSearchSyncDocument[];
|
|
4
|
+
export declare function syncCloudflareAiSearchIndex(options: CloudflareAiSearchSyncOptions): Promise<CloudflareAiSearchSyncResult>;
|
|
5
|
+
//# sourceMappingURL=cloudflare-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-sync.d.ts","sourceRoot":"","sources":["../../src/search/cloudflare-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EACV,8BAA8B,EAC9B,6BAA6B,EAC7B,4BAA4B,EAC7B,MAAM,YAAY,CAAC;AAGpB,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,8BAA8B,EAAE,CAwBlH;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,4BAA4B,CAAC,CAgEvC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { chunkArray, stringifyMetadata, uniqueStrings, wait } from './utils.js';
|
|
2
|
+
export function createCloudflareAiSearchDocuments(records) {
|
|
3
|
+
return records.map((record) => ({
|
|
4
|
+
id: record.id,
|
|
5
|
+
title: record.section ? `${record.title}: ${record.section}` : record.title,
|
|
6
|
+
url: record.url,
|
|
7
|
+
content: [
|
|
8
|
+
`# ${record.section ? `${record.title}: ${record.section}` : record.title}`,
|
|
9
|
+
'',
|
|
10
|
+
`URL: ${record.url}`,
|
|
11
|
+
'',
|
|
12
|
+
record.content
|
|
13
|
+
].join('\n'),
|
|
14
|
+
metadata: stringifyMetadata({
|
|
15
|
+
svedocs: JSON.stringify({
|
|
16
|
+
id: record.id,
|
|
17
|
+
pageId: record.pageId,
|
|
18
|
+
title: record.title,
|
|
19
|
+
section: record.section ?? '',
|
|
20
|
+
url: record.url
|
|
21
|
+
}),
|
|
22
|
+
locale: record.metadata.locale,
|
|
23
|
+
kind: record.metadata.kind
|
|
24
|
+
})
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
export async function syncCloudflareAiSearchIndex(options) {
|
|
28
|
+
const endpoint = options.endpoint ?? createCloudflareAiSearchItemsEndpoint(options);
|
|
29
|
+
const dryRun = options.dryRun ?? (!options.accountId || !options.apiToken);
|
|
30
|
+
const documents = createCloudflareAiSearchDocuments(options.records);
|
|
31
|
+
const uploadIds = documents.map((document) => document.id);
|
|
32
|
+
const deleteIds = createCloudflareDeletePlan(options, uploadIds);
|
|
33
|
+
const result = {
|
|
34
|
+
provider: 'cloudflare-ai-search',
|
|
35
|
+
instanceName: options.instanceName,
|
|
36
|
+
endpoint,
|
|
37
|
+
dryRun,
|
|
38
|
+
indexed: dryRun ? documents.length : 0,
|
|
39
|
+
deleted: dryRun ? deleteIds.length : 0,
|
|
40
|
+
failed: 0,
|
|
41
|
+
planned: {
|
|
42
|
+
uploadIds,
|
|
43
|
+
deleteIds
|
|
44
|
+
},
|
|
45
|
+
errors: []
|
|
46
|
+
};
|
|
47
|
+
if (dryRun)
|
|
48
|
+
return result;
|
|
49
|
+
if (!options.apiToken) {
|
|
50
|
+
throw new Error('CLOUDFLARE_API_TOKEN is required to index Cloudflare AI Search.');
|
|
51
|
+
}
|
|
52
|
+
const requestFetch = options.fetch ?? fetch;
|
|
53
|
+
const batchSize = options.batchSize ?? 10;
|
|
54
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
55
|
+
const retryDelayMs = options.retryDelayMs ?? 250;
|
|
56
|
+
await processBatches(deleteIds, batchSize, async (itemId) => {
|
|
57
|
+
try {
|
|
58
|
+
await withRetry(() => deleteCloudflareAiSearchItem(endpoint, itemId, options.apiToken, requestFetch), {
|
|
59
|
+
maxRetries,
|
|
60
|
+
retryDelayMs
|
|
61
|
+
});
|
|
62
|
+
result.deleted += 1;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
result.failed += 1;
|
|
66
|
+
result.errors.push({
|
|
67
|
+
id: itemId,
|
|
68
|
+
message: error instanceof Error ? error.message : 'Cloudflare AI Search delete failed.'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
await processBatches(documents, batchSize, async (document) => {
|
|
73
|
+
try {
|
|
74
|
+
await withRetry(() => uploadCloudflareAiSearchDocument(endpoint, document, options, requestFetch), {
|
|
75
|
+
maxRetries,
|
|
76
|
+
retryDelayMs
|
|
77
|
+
});
|
|
78
|
+
result.indexed += 1;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
result.failed += 1;
|
|
82
|
+
result.errors.push({
|
|
83
|
+
id: document.id,
|
|
84
|
+
message: error instanceof Error ? error.message : 'Cloudflare AI Search upload failed.'
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
function createCloudflareAiSearchItemsEndpoint(options) {
|
|
91
|
+
if (!options.accountId)
|
|
92
|
+
return 'https://api.cloudflare.com/client/v4/accounts/<account_id>/ai-search/instances/<instance_name>/items';
|
|
93
|
+
const account = encodeURIComponent(options.accountId);
|
|
94
|
+
const instance = encodeURIComponent(options.instanceName);
|
|
95
|
+
if (options.namespace) {
|
|
96
|
+
return `https://api.cloudflare.com/client/v4/accounts/${account}/ai-search/namespaces/${encodeURIComponent(options.namespace)}/instances/${instance}/items`;
|
|
97
|
+
}
|
|
98
|
+
return `https://api.cloudflare.com/client/v4/accounts/${account}/ai-search/instances/${instance}/items`;
|
|
99
|
+
}
|
|
100
|
+
function createCloudflareAiSearchItemEndpoint(itemsEndpoint, itemId) {
|
|
101
|
+
return `${itemsEndpoint.replace(/\/$/, '')}/${encodeURIComponent(itemId)}`;
|
|
102
|
+
}
|
|
103
|
+
function createCloudflareDeletePlan(options, uploadIds) {
|
|
104
|
+
const explicitDeletes = options.deleteIds ?? [];
|
|
105
|
+
if (options.strategy !== 'replace')
|
|
106
|
+
return uniqueStrings(explicitDeletes);
|
|
107
|
+
const uploadSet = new Set(uploadIds);
|
|
108
|
+
return uniqueStrings([
|
|
109
|
+
...explicitDeletes,
|
|
110
|
+
...(options.existingIds ?? []).filter((itemId) => !uploadSet.has(itemId))
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
function createCloudflareItemFilename(document) {
|
|
114
|
+
const safe = document.id.replace(/[^a-zA-Z0-9_-]+/g, '-').replace(/^-|-$/g, '') || 'svedocs-page';
|
|
115
|
+
return `${safe.slice(0, 124)}.md`;
|
|
116
|
+
}
|
|
117
|
+
async function deleteCloudflareAiSearchItem(endpoint, itemId, apiToken, requestFetch) {
|
|
118
|
+
const response = await requestFetch(createCloudflareAiSearchItemEndpoint(endpoint, itemId), {
|
|
119
|
+
method: 'DELETE',
|
|
120
|
+
headers: {
|
|
121
|
+
authorization: `Bearer ${apiToken}`
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
throw new Error(await createCloudflareApiErrorMessage(response, 'delete'));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function uploadCloudflareAiSearchDocument(endpoint, document, options, requestFetch) {
|
|
129
|
+
const form = new FormData();
|
|
130
|
+
form.append('file', new Blob([document.content], { type: 'text/markdown' }), createCloudflareItemFilename(document));
|
|
131
|
+
form.append('metadata', JSON.stringify(document.metadata));
|
|
132
|
+
if (typeof options.waitForCompletion === 'boolean') {
|
|
133
|
+
form.append('wait_for_completion', String(options.waitForCompletion));
|
|
134
|
+
}
|
|
135
|
+
const response = await requestFetch(endpoint, {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: {
|
|
138
|
+
authorization: `Bearer ${options.apiToken}`
|
|
139
|
+
},
|
|
140
|
+
body: form
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new Error(await createCloudflareApiErrorMessage(response, 'upload'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function createCloudflareApiErrorMessage(response, action) {
|
|
147
|
+
const body = await response.text().catch(() => '');
|
|
148
|
+
return `Cloudflare AI Search ${action} returned ${response.status}${body ? `: ${body.slice(0, 500)}` : ''}`;
|
|
149
|
+
}
|
|
150
|
+
async function processBatches(items, batchSize, handler) {
|
|
151
|
+
for (const batch of chunkArray(items, batchSize)) {
|
|
152
|
+
await Promise.all(batch.map((item) => handler(item)));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function withRetry(action, options) {
|
|
156
|
+
let attempt = 0;
|
|
157
|
+
while (true) {
|
|
158
|
+
try {
|
|
159
|
+
await action();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (attempt >= options.maxRetries)
|
|
164
|
+
throw error;
|
|
165
|
+
attempt += 1;
|
|
166
|
+
await wait(options.retryDelayMs * attempt);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CloudflareAiSearchInput, CloudflareAiSearchInstance, CloudflareAiSearchOutput, CloudflareAiSearchNamespace, SearchResult, SearchScope, SearchProvider } from './types.js';
|
|
2
|
+
export declare function createCloudflareAiSearchProvider(input: {
|
|
3
|
+
binding: CloudflareAiSearchInstance | CloudflareAiSearchNamespace;
|
|
4
|
+
instanceName?: string;
|
|
5
|
+
namespace?: boolean;
|
|
6
|
+
}): SearchProvider;
|
|
7
|
+
export declare function runCloudflareAiSearch(binding: CloudflareAiSearchInstance | CloudflareAiSearchNamespace, instanceName: string | undefined, input: CloudflareAiSearchInput, namespace?: boolean): Promise<CloudflareAiSearchOutput>;
|
|
8
|
+
export declare function normalizeCloudflareAiSearchResults(result: CloudflareAiSearchOutput, query: string): SearchResult[];
|
|
9
|
+
export declare function resolveAiSearchInstance(binding: CloudflareAiSearchInstance | CloudflareAiSearchNamespace, instanceName?: string, namespace?: boolean): CloudflareAiSearchInstance;
|
|
10
|
+
export declare function createCloudflareAiSearchScopeFilters(scope?: SearchScope): Record<string, unknown> | undefined;
|
|
11
|
+
//# sourceMappingURL=cloudflare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/search/cloudflare.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,uBAAuB,EACvB,0BAA0B,EAE1B,wBAAwB,EACxB,2BAA2B,EAC3B,YAAY,EACZ,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB,wBAAgB,gCAAgC,CAAC,KAAK,EAAE;IACtD,OAAO,EAAE,0BAA0B,GAAG,2BAA2B,CAAC;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,cAAc,CA4BjB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,0BAA0B,GAAG,2BAA2B,EACjE,YAAY,oBAAY,EACxB,KAAK,EAAE,uBAAuB,EAC9B,SAAS,UAAQ,GAChB,OAAO,CAAC,wBAAwB,CAAC,CASnC;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,wBAAwB,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAKlH;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,0BAA0B,GAAG,2BAA2B,EACjE,YAAY,SAAY,EACxB,SAAS,UAAQ,GAChB,0BAA0B,CAI5B;AAED,wBAAgB,oCAAoC,CAAC,KAAK,GAAE,WAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAMjH"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { matchesSearchScope } from './local.js';
|
|
2
|
+
import { createExcerpt, stringMetadata, stringifyMetadata } from './utils.js';
|
|
3
|
+
export function createCloudflareAiSearchProvider(input) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'cloudflare-ai-search',
|
|
6
|
+
index() {
|
|
7
|
+
// Cloudflare AI Search indexing is intentionally handled by CLI/provider sync.
|
|
8
|
+
},
|
|
9
|
+
async search(query) {
|
|
10
|
+
const maxResults = query.limit ?? 10;
|
|
11
|
+
const filters = createCloudflareAiSearchScopeFilters(query);
|
|
12
|
+
const result = await runCloudflareAiSearch(input.binding, input.instanceName, {
|
|
13
|
+
messages: [
|
|
14
|
+
{
|
|
15
|
+
role: 'user',
|
|
16
|
+
content: query.query
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
ai_search_options: {
|
|
20
|
+
retrieval: {
|
|
21
|
+
retrieval_type: 'hybrid',
|
|
22
|
+
max_num_results: maxResults,
|
|
23
|
+
...(filters ? { filters } : {})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}, input.namespace);
|
|
27
|
+
return normalizeCloudflareAiSearchResults(result, query.query)
|
|
28
|
+
.filter((record) => matchesSearchScope({ metadata: record.metadata }, query));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function runCloudflareAiSearch(binding, instanceName = 'svedocs', input, namespace = false) {
|
|
33
|
+
const instance = resolveAiSearchInstance(binding, instanceName, namespace);
|
|
34
|
+
const aiSearchOptions = createAiSearchOptions(input);
|
|
35
|
+
const messages = input.messages;
|
|
36
|
+
return instance.search({
|
|
37
|
+
...(messages ? { messages } : {}),
|
|
38
|
+
...(!messages && input.query ? { query: input.query } : {}),
|
|
39
|
+
...(aiSearchOptions ? { ai_search_options: aiSearchOptions } : {})
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export function normalizeCloudflareAiSearchResults(result, query) {
|
|
43
|
+
if (result.data?.length) {
|
|
44
|
+
return result.data.map((item, index) => createResultFromItem(item, index, query, result.response ?? result.answer));
|
|
45
|
+
}
|
|
46
|
+
return (result.chunks ?? []).map((chunk, index) => createResultFromChunk(chunk, index, query, result.response ?? result.answer));
|
|
47
|
+
}
|
|
48
|
+
export function resolveAiSearchInstance(binding, instanceName = 'svedocs', namespace = false) {
|
|
49
|
+
return namespace
|
|
50
|
+
? binding.get(instanceName)
|
|
51
|
+
: binding;
|
|
52
|
+
}
|
|
53
|
+
export function createCloudflareAiSearchScopeFilters(scope = {}) {
|
|
54
|
+
const filters = {
|
|
55
|
+
...(scope.locale ? { locale: scope.locale } : {}),
|
|
56
|
+
...(scope.kind ? { kind: scope.kind } : {})
|
|
57
|
+
};
|
|
58
|
+
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
59
|
+
}
|
|
60
|
+
function mergeRetrieval(options, retrieval) {
|
|
61
|
+
return {
|
|
62
|
+
...(options ?? {}),
|
|
63
|
+
retrieval: {
|
|
64
|
+
...(options?.retrieval ?? {}),
|
|
65
|
+
...retrieval
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function createAiSearchOptions(input) {
|
|
70
|
+
const retrieval = typeof input.max_num_results === 'number' ? { max_num_results: input.max_num_results } : {};
|
|
71
|
+
return input.ai_search_options || Object.keys(retrieval).length > 0
|
|
72
|
+
? mergeRetrieval(input.ai_search_options, retrieval)
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
function createResultFromItem(item, index, query, fallbackContent = '') {
|
|
76
|
+
const metadata = normalizeCloudflareMetadata(item.metadata ?? {});
|
|
77
|
+
const section = stringMetadata(metadata.section);
|
|
78
|
+
return {
|
|
79
|
+
id: item.id ?? `cloudflare:${index}`,
|
|
80
|
+
title: item.title ?? stringMetadata(metadata.title) ?? 'Result',
|
|
81
|
+
url: item.url ?? stringMetadata(metadata.url) ?? '#',
|
|
82
|
+
...(section ? { section } : {}),
|
|
83
|
+
excerpt: createExcerpt(item.content ?? item.text ?? fallbackContent, query),
|
|
84
|
+
score: typeof item.score === 'number' ? item.score : 1 / (index + 1),
|
|
85
|
+
metadata
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function createResultFromChunk(chunk, index, query, fallbackContent = '') {
|
|
89
|
+
const metadata = normalizeCloudflareMetadata({
|
|
90
|
+
...(chunk.attributes ?? {}),
|
|
91
|
+
...(chunk.metadata ?? {}),
|
|
92
|
+
...(chunk.item?.metadata ?? {})
|
|
93
|
+
});
|
|
94
|
+
const section = stringMetadata(metadata.section);
|
|
95
|
+
return {
|
|
96
|
+
id: chunk.id ?? stringMetadata(chunk.item?.id) ?? stringMetadata(chunk.item?.key) ?? `cloudflare:${index}`,
|
|
97
|
+
title: stringMetadata(metadata.title) ??
|
|
98
|
+
stringMetadata(chunk.item?.title) ??
|
|
99
|
+
stringMetadata(chunk.item?.filename) ??
|
|
100
|
+
stringMetadata(chunk.item?.key) ??
|
|
101
|
+
'Result',
|
|
102
|
+
url: stringMetadata(metadata.url) ?? stringMetadata(metadata.source_url) ?? '#',
|
|
103
|
+
...(section ? { section } : {}),
|
|
104
|
+
excerpt: createExcerpt(chunk.text ?? chunk.content ?? stringMetadata(metadata.content) ?? fallbackContent, query),
|
|
105
|
+
score: typeof chunk.score === 'number' ? chunk.score : 1 / (index + 1),
|
|
106
|
+
metadata
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function normalizeCloudflareMetadata(metadata) {
|
|
110
|
+
const stringified = stringifyMetadata(metadata);
|
|
111
|
+
const embedded = stringMetadata(metadata.svedocs);
|
|
112
|
+
if (!embedded)
|
|
113
|
+
return stringified;
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(embedded);
|
|
116
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
117
|
+
return stringified;
|
|
118
|
+
return {
|
|
119
|
+
...stringified,
|
|
120
|
+
...stringifyMetadata(parsed)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return stringified;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SvedocsSearchRecord } from '../core.js';
|
|
2
|
+
import type { SearchProvider, SearchQuery, SearchResult, SearchScope } from './types.js';
|
|
3
|
+
export declare function createLocalSearchProvider(records?: SvedocsSearchRecord[]): SearchProvider;
|
|
4
|
+
export declare const localSearchProvider: SearchProvider;
|
|
5
|
+
export declare function searchRecords(records: SvedocsSearchRecord[], input: SearchQuery): SearchResult[];
|
|
6
|
+
export declare function filterSearchRecords(records: SvedocsSearchRecord[], scope?: SearchScope): SvedocsSearchRecord[];
|
|
7
|
+
export declare function matchesSearchScope(record: SvedocsSearchRecord, scope?: SearchScope): boolean;
|
|
8
|
+
export declare function createSearchResponse(records: SvedocsSearchRecord[], request: Request): Promise<Response>;
|
|
9
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/search/local.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgCzF,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,mBAAmB,EAAO,GAAG,cAAc,CAW7F;AAED,eAAO,MAAM,mBAAmB,gBAA8B,CAAC;AAE/D,wBAAgB,aAAa,CAAC,OAAO,EAAE,mBAAmB,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE,CA8BhG;AAyBD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,mBAAmB,EAAE,EAC9B,KAAK,GAAE,WAAgB,GACtB,mBAAmB,EAAE,CAEvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,EAAE,KAAK,GAAE,WAAgB,GAAG,OAAO,CAGhG;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAa9G"}
|