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.
Files changed (286) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +85 -0
  3. package/dist/ai/openai-compatible.d.ts +13 -0
  4. package/dist/ai/openai-compatible.d.ts.map +1 -0
  5. package/dist/ai/openai-compatible.js +92 -0
  6. package/dist/ai/providers.d.ts +20 -0
  7. package/dist/ai/providers.d.ts.map +1 -0
  8. package/dist/ai/providers.js +185 -0
  9. package/dist/ai/rate-limit.d.ts +18 -0
  10. package/dist/ai/rate-limit.d.ts.map +1 -0
  11. package/dist/ai/rate-limit.js +52 -0
  12. package/dist/ai/response.d.ts +4 -0
  13. package/dist/ai/response.d.ts.map +1 -0
  14. package/dist/ai/response.js +151 -0
  15. package/dist/ai/runtime.d.ts +32 -0
  16. package/dist/ai/runtime.d.ts.map +1 -0
  17. package/dist/ai/runtime.js +111 -0
  18. package/dist/ai/types.d.ts +93 -0
  19. package/dist/ai/types.d.ts.map +1 -0
  20. package/dist/ai/types.js +1 -0
  21. package/dist/ai.d.ts +7 -0
  22. package/dist/ai.d.ts.map +1 -0
  23. package/dist/ai.js +6 -0
  24. package/dist/ambient.d.ts +5 -0
  25. package/dist/cloudflare.d.ts +29 -0
  26. package/dist/cloudflare.d.ts.map +1 -0
  27. package/dist/cloudflare.js +149 -0
  28. package/dist/config.d.ts +373 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +299 -0
  31. package/dist/core/checks.d.ts +4 -0
  32. package/dist/core/checks.d.ts.map +1 -0
  33. package/dist/core/checks.js +267 -0
  34. package/dist/core/config.d.ts +6 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +178 -0
  37. package/dist/core/content.d.ts +7 -0
  38. package/dist/core/content.d.ts.map +1 -0
  39. package/dist/core/content.js +340 -0
  40. package/dist/core/i18n.d.ts +5 -0
  41. package/dist/core/i18n.d.ts.map +1 -0
  42. package/dist/core/i18n.js +117 -0
  43. package/dist/core/links.d.ts +5 -0
  44. package/dist/core/links.d.ts.map +1 -0
  45. package/dist/core/links.js +60 -0
  46. package/dist/core/messages.d.ts +149 -0
  47. package/dist/core/messages.d.ts.map +1 -0
  48. package/dist/core/messages.js +163 -0
  49. package/dist/core/navigation.d.ts +5 -0
  50. package/dist/core/navigation.d.ts.map +1 -0
  51. package/dist/core/navigation.js +144 -0
  52. package/dist/core/routes.d.ts +36 -0
  53. package/dist/core/routes.d.ts.map +1 -0
  54. package/dist/core/routes.js +152 -0
  55. package/dist/core/search.d.ts +5 -0
  56. package/dist/core/search.d.ts.map +1 -0
  57. package/dist/core/search.js +41 -0
  58. package/dist/core/types.d.ts +468 -0
  59. package/dist/core/types.d.ts.map +1 -0
  60. package/dist/core/types.js +4 -0
  61. package/dist/core/utils.d.ts +13 -0
  62. package/dist/core/utils.d.ts.map +1 -0
  63. package/dist/core/utils.js +60 -0
  64. package/dist/core.d.ts +9 -0
  65. package/dist/core.d.ts.map +1 -0
  66. package/dist/core.js +8 -0
  67. package/dist/index.d.ts +6 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +3 -0
  70. package/dist/mdx/ast.d.ts +32 -0
  71. package/dist/mdx/ast.d.ts.map +1 -0
  72. package/dist/mdx/ast.js +148 -0
  73. package/dist/mdx/code.d.ts +17 -0
  74. package/dist/mdx/code.d.ts.map +1 -0
  75. package/dist/mdx/code.js +327 -0
  76. package/dist/mdx/compile.d.ts +29 -0
  77. package/dist/mdx/compile.d.ts.map +1 -0
  78. package/dist/mdx/compile.js +59 -0
  79. package/dist/mdx/diff.d.ts +9 -0
  80. package/dist/mdx/diff.d.ts.map +1 -0
  81. package/dist/mdx/diff.js +171 -0
  82. package/dist/mdx/headings.d.ts +4 -0
  83. package/dist/mdx/headings.d.ts.map +1 -0
  84. package/dist/mdx/headings.js +42 -0
  85. package/dist/mdx/links.d.ts +4 -0
  86. package/dist/mdx/links.d.ts.map +1 -0
  87. package/dist/mdx/links.js +144 -0
  88. package/dist/mdx/utils.d.ts +10 -0
  89. package/dist/mdx/utils.d.ts.map +1 -0
  90. package/dist/mdx/utils.js +64 -0
  91. package/dist/og/image.d.ts +25 -0
  92. package/dist/og/image.d.ts.map +1 -0
  93. package/dist/og/image.js +224 -0
  94. package/dist/og/metadata.d.ts +7 -0
  95. package/dist/og/metadata.d.ts.map +1 -0
  96. package/dist/og/metadata.js +198 -0
  97. package/dist/og/response.d.ts +4 -0
  98. package/dist/og/response.d.ts.map +1 -0
  99. package/dist/og/response.js +49 -0
  100. package/dist/og/rss.d.ts +4 -0
  101. package/dist/og/rss.d.ts.map +1 -0
  102. package/dist/og/rss.js +96 -0
  103. package/dist/og/sitemap.d.ts +7 -0
  104. package/dist/og/sitemap.d.ts.map +1 -0
  105. package/dist/og/sitemap.js +90 -0
  106. package/dist/og/types.d.ts +65 -0
  107. package/dist/og/types.d.ts.map +1 -0
  108. package/dist/og/types.js +1 -0
  109. package/dist/og.d.ts +6 -0
  110. package/dist/og.d.ts.map +1 -0
  111. package/dist/og.js +5 -0
  112. package/dist/routes.d.ts +2 -0
  113. package/dist/routes.d.ts.map +1 -0
  114. package/dist/routes.js +1 -0
  115. package/dist/search/algolia.d.ts +10 -0
  116. package/dist/search/algolia.d.ts.map +1 -0
  117. package/dist/search/algolia.js +82 -0
  118. package/dist/search/cloudflare-sync.d.ts +5 -0
  119. package/dist/search/cloudflare-sync.d.ts.map +1 -0
  120. package/dist/search/cloudflare-sync.js +169 -0
  121. package/dist/search/cloudflare.d.ts +11 -0
  122. package/dist/search/cloudflare.d.ts.map +1 -0
  123. package/dist/search/cloudflare.js +126 -0
  124. package/dist/search/local.d.ts +9 -0
  125. package/dist/search/local.d.ts.map +1 -0
  126. package/dist/search/local.js +165 -0
  127. package/dist/search/runtime.d.ts +41 -0
  128. package/dist/search/runtime.d.ts.map +1 -0
  129. package/dist/search/runtime.js +152 -0
  130. package/dist/search/types.d.ts +172 -0
  131. package/dist/search/types.d.ts.map +1 -0
  132. package/dist/search/types.js +1 -0
  133. package/dist/search/typesense.d.ts +11 -0
  134. package/dist/search/typesense.d.ts.map +1 -0
  135. package/dist/search/typesense.js +91 -0
  136. package/dist/search/utils.d.ts +11 -0
  137. package/dist/search/utils.d.ts.map +1 -0
  138. package/dist/search/utils.js +93 -0
  139. package/dist/search.d.ts +8 -0
  140. package/dist/search.d.ts.map +1 -0
  141. package/dist/search.js +7 -0
  142. package/dist/svelte.d.ts +27 -0
  143. package/dist/svelte.d.ts.map +1 -0
  144. package/dist/svelte.js +45 -0
  145. package/dist/testing.d.ts +3 -0
  146. package/dist/testing.d.ts.map +1 -0
  147. package/dist/testing.js +23 -0
  148. package/dist/theme/Article.svelte +145 -0
  149. package/dist/theme/Article.svelte.d.ts +36 -0
  150. package/dist/theme/Article.svelte.d.ts.map +1 -0
  151. package/dist/theme/AskAiPanel.svelte +241 -0
  152. package/dist/theme/AskAiPanel.svelte.d.ts +31 -0
  153. package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
  154. package/dist/theme/Brand.svelte +19 -0
  155. package/dist/theme/Brand.svelte.d.ts +22 -0
  156. package/dist/theme/Brand.svelte.d.ts.map +1 -0
  157. package/dist/theme/Button.svelte +34 -0
  158. package/dist/theme/Button.svelte.d.ts +41 -0
  159. package/dist/theme/Button.svelte.d.ts.map +1 -0
  160. package/dist/theme/Checkbox.svelte +63 -0
  161. package/dist/theme/Checkbox.svelte.d.ts +45 -0
  162. package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
  163. package/dist/theme/CodeBlock.svelte +6 -0
  164. package/dist/theme/CodeBlock.svelte.d.ts +22 -0
  165. package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
  166. package/dist/theme/DocPage.svelte +15 -0
  167. package/dist/theme/DocPage.svelte.d.ts +33 -0
  168. package/dist/theme/DocPage.svelte.d.ts.map +1 -0
  169. package/dist/theme/DocsApp.svelte +194 -0
  170. package/dist/theme/DocsApp.svelte.d.ts +60 -0
  171. package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
  172. package/dist/theme/DocsLayout.svelte +99 -0
  173. package/dist/theme/DocsLayout.svelte.d.ts +39 -0
  174. package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
  175. package/dist/theme/DocsShell.svelte +91 -0
  176. package/dist/theme/DocsShell.svelte.d.ts +34 -0
  177. package/dist/theme/DocsShell.svelte.d.ts.map +1 -0
  178. package/dist/theme/ErrorPage.svelte +113 -0
  179. package/dist/theme/ErrorPage.svelte.d.ts +34 -0
  180. package/dist/theme/ErrorPage.svelte.d.ts.map +1 -0
  181. package/dist/theme/FloatingToolbar.svelte +8 -0
  182. package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
  183. package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
  184. package/dist/theme/Footer.svelte +18 -0
  185. package/dist/theme/Footer.svelte.d.ts +24 -0
  186. package/dist/theme/Footer.svelte.d.ts.map +1 -0
  187. package/dist/theme/FooterLinks.svelte +37 -0
  188. package/dist/theme/FooterLinks.svelte.d.ts +22 -0
  189. package/dist/theme/FooterLinks.svelte.d.ts.map +1 -0
  190. package/dist/theme/FormField.svelte +41 -0
  191. package/dist/theme/FormField.svelte.d.ts +35 -0
  192. package/dist/theme/FormField.svelte.d.ts.map +1 -0
  193. package/dist/theme/HomePage.svelte +284 -0
  194. package/dist/theme/HomePage.svelte.d.ts +65 -0
  195. package/dist/theme/HomePage.svelte.d.ts.map +1 -0
  196. package/dist/theme/Input.svelte +36 -0
  197. package/dist/theme/Input.svelte.d.ts +34 -0
  198. package/dist/theme/Input.svelte.d.ts.map +1 -0
  199. package/dist/theme/LayoutShell.svelte +120 -0
  200. package/dist/theme/LayoutShell.svelte.d.ts +43 -0
  201. package/dist/theme/LayoutShell.svelte.d.ts.map +1 -0
  202. package/dist/theme/LocalizedLink.svelte +21 -0
  203. package/dist/theme/LocalizedLink.svelte.d.ts +38 -0
  204. package/dist/theme/LocalizedLink.svelte.d.ts.map +1 -0
  205. package/dist/theme/MobileNav.svelte +20 -0
  206. package/dist/theme/MobileNav.svelte.d.ts +26 -0
  207. package/dist/theme/MobileNav.svelte.d.ts.map +1 -0
  208. package/dist/theme/Navbar.svelte +69 -0
  209. package/dist/theme/Navbar.svelte.d.ts +29 -0
  210. package/dist/theme/Navbar.svelte.d.ts.map +1 -0
  211. package/dist/theme/PageLayout.svelte +49 -0
  212. package/dist/theme/PageLayout.svelte.d.ts +34 -0
  213. package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
  214. package/dist/theme/PageShell.svelte +98 -0
  215. package/dist/theme/PageShell.svelte.d.ts +35 -0
  216. package/dist/theme/PageShell.svelte.d.ts.map +1 -0
  217. package/dist/theme/PageTools.svelte +87 -0
  218. package/dist/theme/PageTools.svelte.d.ts +25 -0
  219. package/dist/theme/PageTools.svelte.d.ts.map +1 -0
  220. package/dist/theme/RenderError.svelte +62 -0
  221. package/dist/theme/RenderError.svelte.d.ts +31 -0
  222. package/dist/theme/RenderError.svelte.d.ts.map +1 -0
  223. package/dist/theme/RootLayout.svelte +274 -0
  224. package/dist/theme/RootLayout.svelte.d.ts +47 -0
  225. package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
  226. package/dist/theme/RouteRenderError.svelte +53 -0
  227. package/dist/theme/RouteRenderError.svelte.d.ts +34 -0
  228. package/dist/theme/RouteRenderError.svelte.d.ts.map +1 -0
  229. package/dist/theme/SafeRenderError.svelte +49 -0
  230. package/dist/theme/SafeRenderError.svelte.d.ts +33 -0
  231. package/dist/theme/SafeRenderError.svelte.d.ts.map +1 -0
  232. package/dist/theme/ScopeSwitcher.svelte +107 -0
  233. package/dist/theme/ScopeSwitcher.svelte.d.ts +26 -0
  234. package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
  235. package/dist/theme/SearchDialog.svelte +200 -0
  236. package/dist/theme/SearchDialog.svelte.d.ts +31 -0
  237. package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
  238. package/dist/theme/Select.svelte +37 -0
  239. package/dist/theme/Select.svelte.d.ts +42 -0
  240. package/dist/theme/Select.svelte.d.ts.map +1 -0
  241. package/dist/theme/SidebarTree.svelte +148 -0
  242. package/dist/theme/SidebarTree.svelte.d.ts +24 -0
  243. package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
  244. package/dist/theme/SocialNav.svelte +17 -0
  245. package/dist/theme/SocialNav.svelte.d.ts +22 -0
  246. package/dist/theme/SocialNav.svelte.d.ts.map +1 -0
  247. package/dist/theme/TableOfContents.svelte +73 -0
  248. package/dist/theme/TableOfContents.svelte.d.ts +25 -0
  249. package/dist/theme/TableOfContents.svelte.d.ts.map +1 -0
  250. package/dist/theme/Textarea.svelte +33 -0
  251. package/dist/theme/Textarea.svelte.d.ts +33 -0
  252. package/dist/theme/Textarea.svelte.d.ts.map +1 -0
  253. package/dist/theme/ThemeInit.svelte +16 -0
  254. package/dist/theme/ThemeInit.svelte.d.ts +23 -0
  255. package/dist/theme/ThemeInit.svelte.d.ts.map +1 -0
  256. package/dist/theme/ThemeToggle.svelte +37 -0
  257. package/dist/theme/ThemeToggle.svelte.d.ts +23 -0
  258. package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
  259. package/dist/theme/TopNav.svelte +23 -0
  260. package/dist/theme/TopNav.svelte.d.ts +22 -0
  261. package/dist/theme/TopNav.svelte.d.ts.map +1 -0
  262. package/dist/theme/base.css +240 -0
  263. package/dist/theme/defaults.d.ts +4 -0
  264. package/dist/theme/defaults.d.ts.map +1 -0
  265. package/dist/theme/defaults.js +55 -0
  266. package/dist/theme/forms.d.ts +4 -0
  267. package/dist/theme/forms.d.ts.map +1 -0
  268. package/dist/theme/forms.js +3 -0
  269. package/dist/theme/headless.d.ts +62 -0
  270. package/dist/theme/headless.d.ts.map +1 -0
  271. package/dist/theme/headless.js +1064 -0
  272. package/dist/theme/index.d.ts +41 -0
  273. package/dist/theme/index.d.ts.map +1 -0
  274. package/dist/theme/index.js +38 -0
  275. package/dist/theme/portal.d.ts +6 -0
  276. package/dist/theme/portal.d.ts.map +1 -0
  277. package/dist/theme/portal.js +59 -0
  278. package/dist/theme/styles.css +4232 -0
  279. package/dist/theme/types.d.ts +363 -0
  280. package/dist/theme/types.d.ts.map +1 -0
  281. package/dist/theme/types.js +1 -0
  282. package/dist/virtual.d.ts +65 -0
  283. package/dist/vite.d.ts +16 -0
  284. package/dist/vite.d.ts.map +1 -0
  285. package/dist/vite.js +386 -0
  286. package/package.json +132 -0
@@ -0,0 +1,165 @@
1
+ import MiniSearch from 'minisearch';
2
+ import { createExcerpt, jsonResponse, normalizeSearchText, tokenizeSearchQuery, tokenizeSearchText } from './utils.js';
3
+ const miniSearchCache = new WeakMap();
4
+ const localSearchBoost = {
5
+ title: 5,
6
+ section: 4,
7
+ url: 1.5,
8
+ metadataText: 0.8,
9
+ content: 1
10
+ };
11
+ export function createLocalSearchProvider(records = []) {
12
+ let indexed = records;
13
+ return {
14
+ name: 'local-json',
15
+ index(nextRecords) {
16
+ indexed = nextRecords;
17
+ },
18
+ search(input) {
19
+ return searchRecords(indexed, input);
20
+ }
21
+ };
22
+ }
23
+ export const localSearchProvider = createLocalSearchProvider();
24
+ export function searchRecords(records, input) {
25
+ const query = normalizeSearchText(input.query);
26
+ if (!query)
27
+ return [];
28
+ const limit = input.limit ?? 10;
29
+ const entry = getMiniSearchIndex(records);
30
+ const primary = runLocalSearch(entry, input, 'AND');
31
+ const selected = primary.slice(0, limit);
32
+ if (selected.length < limit && tokenizeSearchQuery(input.query).length > 1) {
33
+ const seen = new Set(selected.map((hit) => hit.record.id));
34
+ const fallbackScoreCeiling = selected.at(-1)?.score;
35
+ selected.push(...runLocalSearch(entry, input, 'OR')
36
+ .filter((hit) => !seen.has(hit.record.id))
37
+ .map((hit) => ({
38
+ ...hit,
39
+ score: fallbackScoreCeiling ? Math.min(hit.score * 0.6, fallbackScoreCeiling * 0.95) : hit.score * 0.6
40
+ }))
41
+ .slice(0, limit - selected.length));
42
+ }
43
+ return selected.map(({ record, score }) => ({
44
+ id: record.id,
45
+ title: record.title,
46
+ url: record.url,
47
+ ...(record.section ? { section: record.section } : {}),
48
+ excerpt: createExcerpt(record.content, input.query),
49
+ score,
50
+ metadata: record.metadata
51
+ }));
52
+ }
53
+ function runLocalSearch(entry, input, combineWith) {
54
+ const scoped = entry.index.search(input.query, {
55
+ prefix: true,
56
+ fuzzy: (term) => term.length > 4 ? 0.18 : false,
57
+ combineWith,
58
+ boost: localSearchBoost
59
+ });
60
+ return scoped
61
+ .map((hit) => {
62
+ const record = entry.recordsById.get(hit.id);
63
+ return record ? { record, score: scoreLocalSearchHit(record, hit.score, input.query) } : undefined;
64
+ })
65
+ .filter((item) => Boolean(item))
66
+ .filter((item) => matchesSearchScope(item.record, input))
67
+ .sort((a, b) => b.score - a.score)
68
+ .map((hit) => ({ ...hit, score: Number(hit.score.toFixed(4)) }));
69
+ }
70
+ export function filterSearchRecords(records, scope = {}) {
71
+ return records.filter((record) => matchesSearchScope(record, scope));
72
+ }
73
+ export function matchesSearchScope(record, scope = {}) {
74
+ return matchesMetadata(record.metadata.locale, scope.locale)
75
+ && matchesMetadata(record.metadata.kind, scope.kind);
76
+ }
77
+ export async function createSearchResponse(records, request) {
78
+ const url = new URL(request.url);
79
+ const query = url.searchParams.get('q') ?? url.searchParams.get('query') ?? '';
80
+ const limit = clampLimit(Number(url.searchParams.get('limit') ?? 10));
81
+ const locale = readScopeParam(url, 'locale');
82
+ const kind = readScopeParam(url, 'kind');
83
+ const results = searchRecords(records, {
84
+ query,
85
+ limit,
86
+ ...(locale ? { locale } : {}),
87
+ ...(kind ? { kind } : {})
88
+ });
89
+ return jsonResponse({ query, results });
90
+ }
91
+ function getMiniSearchIndex(records) {
92
+ const cached = miniSearchCache.get(records);
93
+ if (cached)
94
+ return cached;
95
+ const documents = records.map(toMiniSearchDocument);
96
+ const index = new MiniSearch({
97
+ fields: ['title', 'section', 'url', 'content', 'metadataText'],
98
+ storeFields: ['id', 'title', 'section', 'url', 'content', 'metadata'],
99
+ idField: 'id',
100
+ searchOptions: {
101
+ prefix: true,
102
+ fuzzy: 0.18
103
+ },
104
+ tokenize: tokenizeSearchText,
105
+ processTerm: (term) => normalizeSearchText(term) || false
106
+ });
107
+ index.addAll(documents);
108
+ const entry = {
109
+ index,
110
+ recordsById: new Map(records.map((record) => [record.id, record]))
111
+ };
112
+ miniSearchCache.set(records, entry);
113
+ return entry;
114
+ }
115
+ function toMiniSearchDocument(record) {
116
+ return {
117
+ id: record.id,
118
+ title: record.title,
119
+ section: record.section ?? '',
120
+ url: record.url,
121
+ content: record.content,
122
+ metadataText: Object.values(record.metadata).flat().join(' '),
123
+ metadata: record.metadata
124
+ };
125
+ }
126
+ function matchesMetadata(value, expected) {
127
+ if (!expected)
128
+ return true;
129
+ if (Array.isArray(value))
130
+ return value.some((item) => String(item) === expected);
131
+ return value === expected;
132
+ }
133
+ function scoreLocalSearchHit(record, score, query) {
134
+ const normalizedQuery = normalizeSearchText(query);
135
+ const title = normalizeSearchText(record.title);
136
+ const section = normalizeSearchText(record.section ?? '');
137
+ const url = normalizeSearchText(record.url);
138
+ const sourcePath = normalizeSearchText(typeof record.metadata.sourcePath === 'string' ? record.metadata.sourcePath : '');
139
+ const terms = tokenizeSearchQuery(query);
140
+ let multiplier = 1;
141
+ if (title === normalizedQuery || section === normalizedQuery) {
142
+ multiplier *= 1.6;
143
+ }
144
+ else if (title.startsWith(normalizedQuery) || section.startsWith(normalizedQuery)) {
145
+ multiplier *= 1.35;
146
+ }
147
+ else if (title.includes(normalizedQuery) || section.includes(normalizedQuery)) {
148
+ multiplier *= 1.2;
149
+ }
150
+ else if (terms.length > 1 && terms.every((term) => title.includes(term) || section.includes(term))) {
151
+ multiplier *= 1.15;
152
+ }
153
+ if (url.includes(normalizedQuery) || sourcePath.includes(normalizedQuery))
154
+ multiplier *= 1.1;
155
+ return score * multiplier;
156
+ }
157
+ function readScopeParam(url, key) {
158
+ const value = url.searchParams.get(key);
159
+ return value?.trim() || undefined;
160
+ }
161
+ function clampLimit(value) {
162
+ if (!Number.isFinite(value))
163
+ return 10;
164
+ return Math.min(50, Math.max(1, Math.floor(value)));
165
+ }
@@ -0,0 +1,41 @@
1
+ import type { SvedocsResolvedConfig, SvedocsSearchRecord } from '../core.js';
2
+ import type { CloudflareAiSearchInstance, CloudflareAiSearchNamespace, SearchProvider, SearchQuery } from './types.js';
3
+ export interface SvedocsSearchRuntimeEnv {
4
+ SVEDOCS_AI_SEARCH?: CloudflareAiSearchInstance | CloudflareAiSearchNamespace | undefined;
5
+ ALGOLIA_APP_ID?: string | undefined;
6
+ ALGOLIA_SEARCH_KEY?: string | undefined;
7
+ ALGOLIA_API_KEY?: string | undefined;
8
+ ALGOLIA_INDEX_NAME?: string | undefined;
9
+ SVEDOCS_ALGOLIA_APP_ID?: string | undefined;
10
+ SVEDOCS_ALGOLIA_SEARCH_KEY?: string | undefined;
11
+ SVEDOCS_ALGOLIA_API_KEY?: string | undefined;
12
+ SVEDOCS_ALGOLIA_INDEX_NAME?: string | undefined;
13
+ TYPESENSE_HOST?: string | undefined;
14
+ TYPESENSE_URL?: string | undefined;
15
+ TYPESENSE_SEARCH_KEY?: string | undefined;
16
+ TYPESENSE_API_KEY?: string | undefined;
17
+ TYPESENSE_COLLECTION?: string | undefined;
18
+ TYPESENSE_COLLECTION_NAME?: string | undefined;
19
+ SVEDOCS_TYPESENSE_HOST?: string | undefined;
20
+ SVEDOCS_TYPESENSE_URL?: string | undefined;
21
+ SVEDOCS_TYPESENSE_SEARCH_KEY?: string | undefined;
22
+ SVEDOCS_TYPESENSE_API_KEY?: string | undefined;
23
+ SVEDOCS_TYPESENSE_COLLECTION?: string | undefined;
24
+ SVEDOCS_TYPESENSE_COLLECTION_NAME?: string | undefined;
25
+ [key: string]: unknown;
26
+ }
27
+ export interface CreateConfiguredSearchProviderOptions {
28
+ config: Pick<SvedocsResolvedConfig, 'search' | 'cloudflare'>;
29
+ records?: SvedocsSearchRecord[];
30
+ env?: SvedocsSearchRuntimeEnv;
31
+ provider?: string;
32
+ fetch?: typeof fetch;
33
+ }
34
+ export interface CreateConfiguredSearchResponseOptions extends Omit<CreateConfiguredSearchProviderOptions, 'config' | 'records' | 'provider'> {
35
+ provider?: string;
36
+ }
37
+ export declare function createConfiguredSearchProvider(options: CreateConfiguredSearchProviderOptions): SearchProvider;
38
+ export declare function createConfiguredSearchResponse(config: Pick<SvedocsResolvedConfig, 'search' | 'cloudflare'>, records: SvedocsSearchRecord[], request: Request, options?: CreateConfiguredSearchResponseOptions): Promise<Response>;
39
+ export declare function readSearchQuery(request: Request): SearchQuery;
40
+ export declare function readProviderParam(request: Request): string | undefined;
41
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/search/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAK7E,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,cAAc,EACd,WAAW,EAEZ,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,uBAAuB;IACtC,iBAAiB,CAAC,EAAE,0BAA0B,GAAG,2BAA2B,GAAG,SAAS,CAAC;IACzF,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,0BAA0B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,0BAA0B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,yBAAyB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,4BAA4B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,yBAAyB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,4BAA4B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,iCAAiC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qCAAqC;IACpD,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAC;IAC7D,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC,GAAG,CAAC,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,qCAAsC,SAAQ,IAAI,CAAC,qCAAqC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IAC3I,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qCAAqC,GAAG,cAAc,CAmD7G;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,YAAY,CAAC,EAC5D,OAAO,EAAE,mBAAmB,EAAE,EAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,qCAA0C,GAClD,OAAO,CAAC,QAAQ,CAAC,CAkBnB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAU7D;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE"}
@@ -0,0 +1,152 @@
1
+ import { createAlgoliaSearchProvider } from './algolia.js';
2
+ import { createCloudflareAiSearchProvider } from './cloudflare.js';
3
+ import { createLocalSearchProvider, createSearchResponse } from './local.js';
4
+ import { createTypesenseSearchProvider } from './typesense.js';
5
+ import { jsonResponse } from './utils.js';
6
+ export function createConfiguredSearchProvider(options) {
7
+ const provider = normalizeProviderName(options.provider ?? options.config.search.provider);
8
+ const records = options.records ?? [];
9
+ if (!options.config.search.enabled)
10
+ return createDisabledSearchProvider();
11
+ if (provider === 'cloudflare-ai-search') {
12
+ const binding = readCloudflareAiSearchBinding(options.env, options.config);
13
+ if (binding) {
14
+ return createCloudflareAiSearchProvider({
15
+ binding,
16
+ instanceName: options.config.cloudflare.aiSearch.instanceName,
17
+ namespace: Boolean(options.config.cloudflare.aiSearch.namespace)
18
+ });
19
+ }
20
+ return createLocalSearchProvider(records);
21
+ }
22
+ if (provider === 'algolia') {
23
+ const appId = readFirstEnv(options.env, ['SVEDOCS_ALGOLIA_APP_ID', 'ALGOLIA_APP_ID']);
24
+ const apiKey = readFirstEnv(options.env, ['SVEDOCS_ALGOLIA_SEARCH_KEY', 'SVEDOCS_ALGOLIA_API_KEY', 'ALGOLIA_SEARCH_KEY', 'ALGOLIA_API_KEY']);
25
+ const indexName = readFirstEnv(options.env, ['SVEDOCS_ALGOLIA_INDEX_NAME', 'ALGOLIA_INDEX_NAME']);
26
+ if (appId && apiKey && indexName) {
27
+ return createAlgoliaSearchProvider({
28
+ appId,
29
+ apiKey,
30
+ indexName,
31
+ filters: createAlgoliaScopeFilters,
32
+ ...(options.fetch ? { fetch: options.fetch } : {})
33
+ });
34
+ }
35
+ return createLocalSearchProvider(records);
36
+ }
37
+ if (provider === 'typesense') {
38
+ const host = readFirstEnv(options.env, ['SVEDOCS_TYPESENSE_HOST', 'SVEDOCS_TYPESENSE_URL', 'TYPESENSE_HOST', 'TYPESENSE_URL']);
39
+ const apiKey = readFirstEnv(options.env, ['SVEDOCS_TYPESENSE_SEARCH_KEY', 'SVEDOCS_TYPESENSE_API_KEY', 'TYPESENSE_SEARCH_KEY', 'TYPESENSE_API_KEY']);
40
+ const collection = readFirstEnv(options.env, [
41
+ 'SVEDOCS_TYPESENSE_COLLECTION',
42
+ 'SVEDOCS_TYPESENSE_COLLECTION_NAME',
43
+ 'TYPESENSE_COLLECTION',
44
+ 'TYPESENSE_COLLECTION_NAME'
45
+ ]) ?? 'docs';
46
+ if (host && apiKey) {
47
+ return createTypesenseSearchProvider({
48
+ host,
49
+ apiKey,
50
+ collection,
51
+ filterBy: createTypesenseScopeFilters,
52
+ ...(options.fetch ? { fetch: options.fetch } : {})
53
+ });
54
+ }
55
+ return createLocalSearchProvider(records);
56
+ }
57
+ return createLocalSearchProvider(records);
58
+ }
59
+ export async function createConfiguredSearchResponse(config, records, request, options = {}) {
60
+ if (!config.search.enabled) {
61
+ return jsonResponse({ query: readSearchQuery(request).query, results: [] });
62
+ }
63
+ const providerName = options.provider ?? config.search.provider;
64
+ const provider = createConfiguredSearchProvider({
65
+ config,
66
+ records,
67
+ provider: providerName,
68
+ ...(options.env ? { env: options.env } : {}),
69
+ ...(options.fetch ? { fetch: options.fetch } : {})
70
+ });
71
+ if (provider.name === 'local-json' || provider.name === 'disabled') {
72
+ return createSearchResponse(records, request);
73
+ }
74
+ const query = readSearchQuery(request);
75
+ const results = await provider.search(query);
76
+ return jsonResponse({ query: query.query, provider: provider.name, results });
77
+ }
78
+ export function readSearchQuery(request) {
79
+ const url = new URL(request.url);
80
+ const locale = readScopeParam(url, 'locale');
81
+ const kind = readScopeParam(url, 'kind');
82
+ return {
83
+ query: url.searchParams.get('q') ?? url.searchParams.get('query') ?? '',
84
+ limit: clampLimit(Number(url.searchParams.get('limit') ?? 10)),
85
+ ...(locale ? { locale } : {}),
86
+ ...(kind ? { kind } : {})
87
+ };
88
+ }
89
+ export function readProviderParam(request) {
90
+ const provider = new URL(request.url).searchParams.get('provider')?.trim();
91
+ return provider || undefined;
92
+ }
93
+ function createDisabledSearchProvider() {
94
+ return {
95
+ name: 'disabled',
96
+ index() { },
97
+ search() {
98
+ return [];
99
+ }
100
+ };
101
+ }
102
+ function readCloudflareAiSearchBinding(env, config) {
103
+ const bindingName = config.cloudflare.aiSearch.binding;
104
+ const dynamicBinding = env?.[bindingName];
105
+ if (isCloudflareAiSearchBinding(dynamicBinding))
106
+ return dynamicBinding;
107
+ if (isCloudflareAiSearchBinding(env?.SVEDOCS_AI_SEARCH))
108
+ return env.SVEDOCS_AI_SEARCH;
109
+ return undefined;
110
+ }
111
+ function isCloudflareAiSearchBinding(value) {
112
+ if (!value || typeof value !== 'object')
113
+ return false;
114
+ return 'search' in value || 'get' in value;
115
+ }
116
+ function readFirstEnv(env, keys) {
117
+ for (const key of keys) {
118
+ const value = env?.[key];
119
+ if (typeof value === 'string' && value.trim())
120
+ return value.trim();
121
+ }
122
+ return undefined;
123
+ }
124
+ function createAlgoliaScopeFilters(query) {
125
+ return [
126
+ query.locale ? `locale:${quoteAlgoliaFilterValue(query.locale)}` : undefined,
127
+ query.kind ? `kind:${quoteAlgoliaFilterValue(query.kind)}` : undefined
128
+ ].filter(Boolean).join(' AND ') || undefined;
129
+ }
130
+ function quoteAlgoliaFilterValue(value) {
131
+ return /^[\w-]+$/.test(value) ? value : JSON.stringify(value);
132
+ }
133
+ function createTypesenseScopeFilters(query) {
134
+ return [
135
+ query.locale ? `locale:=${quoteTypesenseFilterValue(query.locale)}` : undefined,
136
+ query.kind ? `kind:=${quoteTypesenseFilterValue(query.kind)}` : undefined
137
+ ].filter(Boolean).join(' && ') || undefined;
138
+ }
139
+ function quoteTypesenseFilterValue(value) {
140
+ return /^[\w-]+$/.test(value) ? value : `\`${value.replace(/`/g, '\\`')}\``;
141
+ }
142
+ function normalizeProviderName(provider) {
143
+ return provider?.trim().toLowerCase() || 'local';
144
+ }
145
+ function readScopeParam(url, key) {
146
+ return url.searchParams.get(key)?.trim() || undefined;
147
+ }
148
+ function clampLimit(value) {
149
+ if (!Number.isFinite(value))
150
+ return 10;
151
+ return Math.min(50, Math.max(1, Math.floor(value)));
152
+ }
@@ -0,0 +1,172 @@
1
+ import type { SvedocsSearchRecord } from '../core.js';
2
+ export interface SearchQuery {
3
+ query: string;
4
+ limit?: number;
5
+ locale?: string;
6
+ kind?: string;
7
+ }
8
+ export interface SearchScope {
9
+ locale?: string;
10
+ kind?: string;
11
+ }
12
+ export interface SearchResult {
13
+ id: string;
14
+ title: string;
15
+ url: string;
16
+ excerpt: string;
17
+ score: number;
18
+ section?: string;
19
+ metadata: SvedocsSearchRecord['metadata'];
20
+ }
21
+ export interface SearchProvider {
22
+ name: string;
23
+ index(records: SvedocsSearchRecord[]): Promise<void> | void;
24
+ search(query: SearchQuery): Promise<SearchResult[]> | SearchResult[];
25
+ }
26
+ export interface CloudflareAiSearchSyncDocument {
27
+ id: string;
28
+ title: string;
29
+ url: string;
30
+ content: string;
31
+ metadata: Record<string, string>;
32
+ }
33
+ export interface CloudflareAiSearchSyncOptions {
34
+ records: SvedocsSearchRecord[];
35
+ accountId?: string;
36
+ apiToken?: string;
37
+ instanceName: string;
38
+ namespace?: string;
39
+ endpoint?: string;
40
+ dryRun?: boolean;
41
+ waitForCompletion?: boolean;
42
+ strategy?: 'append' | 'replace';
43
+ deleteIds?: string[];
44
+ existingIds?: string[];
45
+ batchSize?: number;
46
+ maxRetries?: number;
47
+ retryDelayMs?: number;
48
+ fetch?: typeof fetch;
49
+ }
50
+ export interface CloudflareAiSearchSyncResult {
51
+ provider: 'cloudflare-ai-search';
52
+ instanceName: string;
53
+ endpoint: string;
54
+ dryRun: boolean;
55
+ indexed: number;
56
+ deleted: number;
57
+ failed: number;
58
+ planned: {
59
+ uploadIds: string[];
60
+ deleteIds: string[];
61
+ };
62
+ errors: Array<{
63
+ id: string;
64
+ message: string;
65
+ }>;
66
+ }
67
+ export interface CloudflareAiSearchInstance {
68
+ search(input: CloudflareAiSearchInput): Promise<CloudflareAiSearchOutput>;
69
+ chatCompletions?(input: CloudflareAiSearchChatInput): Promise<CloudflareAiSearchChatOutput | ReadableStream<Uint8Array> | Response>;
70
+ items?: CloudflareAiSearchItemsBinding;
71
+ }
72
+ export interface CloudflareAiSearchItemsBinding {
73
+ upload(input: File | Blob | ArrayBuffer | string, options?: CloudflareAiSearchItemUploadOptions): Promise<unknown>;
74
+ uploadAndPoll?(input: File | Blob | ArrayBuffer | string, options?: CloudflareAiSearchItemUploadOptions): Promise<unknown>;
75
+ delete?(itemId: string): Promise<unknown>;
76
+ get?(itemId: string): Promise<unknown>;
77
+ list?(options?: {
78
+ limit?: number;
79
+ cursor?: string;
80
+ }): Promise<unknown>;
81
+ }
82
+ export interface CloudflareAiSearchItemUploadOptions {
83
+ id?: string;
84
+ metadata?: Record<string, string>;
85
+ contentType?: string;
86
+ filename?: string;
87
+ }
88
+ export interface CloudflareAiSearchMessage {
89
+ role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
90
+ content: string;
91
+ }
92
+ export interface CloudflareAiSearchInput {
93
+ query?: string;
94
+ messages?: CloudflareAiSearchMessage[];
95
+ ai_search_options?: {
96
+ instance_ids?: string[];
97
+ query_rewrite?: boolean | {
98
+ enabled?: boolean;
99
+ model?: string;
100
+ };
101
+ retrieval?: {
102
+ retrieval_type?: 'vector' | 'keyword' | 'hybrid';
103
+ match_threshold?: number;
104
+ max_num_results?: number;
105
+ filters?: Record<string, unknown>;
106
+ context_expansion?: number;
107
+ fusion_method?: 'rrf' | 'max';
108
+ keyword_match_mode?: string;
109
+ reranking?: {
110
+ enabled?: boolean;
111
+ model?: string;
112
+ };
113
+ };
114
+ };
115
+ max_num_results?: number;
116
+ }
117
+ export interface CloudflareAiSearchItem {
118
+ id?: string;
119
+ title?: string;
120
+ url?: string;
121
+ content?: string;
122
+ text?: string;
123
+ score?: number;
124
+ metadata?: Record<string, unknown>;
125
+ }
126
+ export interface CloudflareAiSearchChunk {
127
+ id?: string;
128
+ type?: string;
129
+ text?: string;
130
+ content?: string;
131
+ score?: number;
132
+ metadata?: Record<string, unknown>;
133
+ attributes?: Record<string, unknown>;
134
+ item?: {
135
+ id?: string;
136
+ key?: string;
137
+ filename?: string;
138
+ title?: string;
139
+ metadata?: Record<string, unknown>;
140
+ };
141
+ }
142
+ export interface CloudflareAiSearchOutput {
143
+ response?: string;
144
+ answer?: string;
145
+ data?: CloudflareAiSearchItem[];
146
+ chunks?: CloudflareAiSearchChunk[];
147
+ }
148
+ export interface CloudflareAiSearchChatInput {
149
+ messages: CloudflareAiSearchMessage[];
150
+ ai_search_options?: CloudflareAiSearchInput['ai_search_options'];
151
+ model?: string;
152
+ stream?: boolean;
153
+ }
154
+ export interface CloudflareAiSearchChatOutput extends CloudflareAiSearchOutput {
155
+ choices?: Array<{
156
+ text?: string;
157
+ message?: {
158
+ content?: string;
159
+ };
160
+ }>;
161
+ citations?: Array<{
162
+ title?: string;
163
+ url?: string;
164
+ section?: string;
165
+ }>;
166
+ }
167
+ export interface CloudflareAiSearchNamespace {
168
+ get(instanceName: string): CloudflareAiSearchInstance;
169
+ search?(input: CloudflareAiSearchInput): Promise<CloudflareAiSearchOutput>;
170
+ chatCompletions?(input: CloudflareAiSearchChatInput): Promise<CloudflareAiSearchChatOutput | ReadableStream<Uint8Array> | Response>;
171
+ }
172
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { SearchProvider, SearchQuery } from './types.js';
2
+ export interface TypesenseSearchProviderOptions {
3
+ host: string;
4
+ apiKey: string;
5
+ collection: string | ((query: SearchQuery) => string);
6
+ queryBy?: string | string[];
7
+ filterBy?: string | ((query: SearchQuery) => string | undefined);
8
+ fetch?: typeof fetch;
9
+ }
10
+ export declare function createTypesenseSearchProvider(options: TypesenseSearchProviderOptions): SearchProvider;
11
+ //# sourceMappingURL=typesense.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typesense.d.ts","sourceRoot":"","sources":["../../src/search/typesense.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAgB,MAAM,YAAY,CAAC;AAG5E,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACtD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IACjE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AA6BD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,cAAc,CAsBrG"}
@@ -0,0 +1,91 @@
1
+ import { matchesSearchScope } from './local.js';
2
+ import { createExcerpt, stringMetadata, stringifyMetadata } from './utils.js';
3
+ export function createTypesenseSearchProvider(options) {
4
+ return {
5
+ name: 'typesense',
6
+ index() {
7
+ // Typesense indexing is commonly handled by a crawler or CI sync script.
8
+ },
9
+ async search(query) {
10
+ const requestFetch = options.fetch ?? fetch;
11
+ const response = await requestFetch(createTypesenseSearchUrl(options, query), {
12
+ headers: {
13
+ 'x-typesense-api-key': options.apiKey
14
+ }
15
+ });
16
+ if (!response.ok) {
17
+ throw new Error(`Typesense search returned ${response.status}: ${(await response.text()).slice(0, 500)}`);
18
+ }
19
+ const payload = await response.json();
20
+ return (payload.hits ?? [])
21
+ .map((hit, index) => createTypesenseResult(hit, index, query.query))
22
+ .filter((result) => matchesSearchScope({ metadata: result.metadata }, query));
23
+ }
24
+ };
25
+ }
26
+ function createTypesenseSearchUrl(options, query) {
27
+ const collection = typeof options.collection === 'function' ? options.collection(query) : options.collection;
28
+ const base = options.host.replace(/\/$/, '');
29
+ const url = new URL(`${base}/collections/${encodeURIComponent(collection)}/documents/search`);
30
+ url.searchParams.set('q', query.query || '*');
31
+ url.searchParams.set('query_by', Array.isArray(options.queryBy) ? options.queryBy.join(',') : options.queryBy ?? 'title,section,content');
32
+ url.searchParams.set('per_page', String(query.limit ?? 10));
33
+ const filterBy = typeof options.filterBy === 'function' ? options.filterBy(query) : options.filterBy;
34
+ if (filterBy)
35
+ url.searchParams.set('filter_by', filterBy);
36
+ return url.toString();
37
+ }
38
+ function createTypesenseResult(hit, index, query) {
39
+ const document = hit.document ?? {};
40
+ const metadata = normalizeTypesenseMetadata(document);
41
+ const section = stringMetadata(document.section) ?? stringMetadata(metadata.section);
42
+ const content = stringMetadata(document.content) ?? stringMetadata(metadata.content) ?? stringMetadata(document.title) ?? '';
43
+ return {
44
+ id: stringMetadata(document.id) ?? stringMetadata(document.objectID) ?? `typesense:${index}`,
45
+ title: stringMetadata(document.title) ?? stringMetadata(metadata.title) ?? 'Result',
46
+ url: createTypesenseUrl(document, metadata),
47
+ ...(section ? { section } : {}),
48
+ excerpt: createExcerpt(content, query),
49
+ score: createTypesenseScore(hit, index),
50
+ metadata
51
+ };
52
+ }
53
+ function createTypesenseUrl(document, metadata) {
54
+ const url = stringMetadata(document.url) ?? stringMetadata(metadata.url) ?? '#';
55
+ const anchor = stringMetadata(document.anchor) ?? stringMetadata(metadata.anchor);
56
+ if (!anchor || url.includes('#'))
57
+ return url;
58
+ return `${url}#${anchor}`;
59
+ }
60
+ function normalizeTypesenseMetadata(document) {
61
+ const metadata = stringifyMetadata({
62
+ ...(document.metadata ?? {}),
63
+ locale: document.locale ?? document.metadata?.locale,
64
+ kind: document.kind ?? document.metadata?.kind
65
+ });
66
+ const embedded = stringMetadata(document.svedocs) ?? stringMetadata(document.metadata?.svedocs);
67
+ if (!embedded)
68
+ return metadata;
69
+ try {
70
+ const parsed = JSON.parse(embedded);
71
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
72
+ return metadata;
73
+ return {
74
+ ...metadata,
75
+ ...stringifyMetadata(parsed)
76
+ };
77
+ }
78
+ catch {
79
+ return metadata;
80
+ }
81
+ }
82
+ function createTypesenseScore(hit, index) {
83
+ if (typeof hit.text_match === 'number')
84
+ return hit.text_match;
85
+ const score = hit.text_match_info?.score;
86
+ if (typeof score === 'number')
87
+ return score;
88
+ if (typeof score === 'string' && Number.isFinite(Number(score)))
89
+ return Number(score);
90
+ return 1 / (index + 1);
91
+ }
@@ -0,0 +1,11 @@
1
+ export declare function createExcerpt(content: string, query: string): string;
2
+ export declare function normalizeSearchText(value: string): string;
3
+ export declare function tokenizeSearchText(value: string): string[];
4
+ export declare function tokenizeSearchQuery(query: string): string[];
5
+ export declare function stringMetadata(value: unknown): string | undefined;
6
+ export declare function stringifyMetadata(metadata: Record<string, unknown>): Record<string, string>;
7
+ export declare function uniqueStrings(values: string[]): string[];
8
+ export declare function jsonResponse(value: unknown, status?: number): Response;
9
+ export declare function chunkArray<T>(items: T[], size: number): T[][];
10
+ export declare function wait(ms: number): Promise<void>;
11
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/search/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWpE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI1D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE3D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAEjE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM3F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAExD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,SAAM,GAAG,QAAQ,CAOnE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAO7D;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9C"}