@andesine/pages 0.0.0-stage → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/LICENSE +21 -0
  3. package/README.md +92 -2
  4. package/dist/answers.d.ts +6 -0
  5. package/dist/astro/content.astro +118 -0
  6. package/dist/astro/middleware.js +39 -0
  7. package/dist/astro/middleware.js.map +1 -0
  8. package/dist/astro/routes/answers.js +129 -0
  9. package/dist/astro/routes/answers.js.map +1 -0
  10. package/dist/astro/routes/entry-pages.js +22 -0
  11. package/dist/astro/routes/entry-pages.js.map +1 -0
  12. package/dist/astro/routes/llms-full.js +12 -0
  13. package/dist/astro/routes/llms-full.js.map +1 -0
  14. package/dist/astro/routes/llms.js +12 -0
  15. package/dist/astro/routes/llms.js.map +1 -0
  16. package/dist/astro/routes/markdown-alias.js +14 -0
  17. package/dist/astro/routes/markdown-alias.js.map +1 -0
  18. package/dist/astro/routes/markdown.js +12 -0
  19. package/dist/astro/routes/markdown.js.map +1 -0
  20. package/dist/astro/routes/not-found.astro +12 -0
  21. package/dist/astro/routes/robots.js +9 -0
  22. package/dist/astro/routes/robots.js.map +1 -0
  23. package/dist/astro/routes/search.js +42 -0
  24. package/dist/astro/routes/search.js.map +1 -0
  25. package/dist/astro/routes/sitemap.js +12 -0
  26. package/dist/astro/routes/sitemap.js.map +1 -0
  27. package/dist/astro/routes/skill.js +13 -0
  28. package/dist/astro/routes/skill.js.map +1 -0
  29. package/dist/astro/routes/skills.js +24 -0
  30. package/dist/astro/routes/skills.js.map +1 -0
  31. package/dist/astro/routes/social.js +91 -0
  32. package/dist/astro/routes/social.js.map +1 -0
  33. package/dist/astro/routes/static.astro +53 -0
  34. package/dist/astro/routes/well-known-llms.js +2 -0
  35. package/dist/astro.d.ts +34 -0
  36. package/dist/astro.js +60 -0
  37. package/dist/astro.js.map +1 -0
  38. package/dist/chunks/assets-D6WJmZ05.js +21 -0
  39. package/dist/chunks/assets-D6WJmZ05.js.map +1 -0
  40. package/dist/chunks/catalog-DU4BUrpw.js +28 -0
  41. package/dist/chunks/catalog-DU4BUrpw.js.map +1 -0
  42. package/dist/chunks/config-CuH5JSI4.js +184 -0
  43. package/dist/chunks/config-CuH5JSI4.js.map +1 -0
  44. package/dist/chunks/files-BxqiQzFj.js +62 -0
  45. package/dist/chunks/files-BxqiQzFj.js.map +1 -0
  46. package/dist/chunks/hash-D4piIIQ6.js +11 -0
  47. package/dist/chunks/hash-D4piIIQ6.js.map +1 -0
  48. package/dist/chunks/index-D8wL2B5s.d.ts +571 -0
  49. package/dist/chunks/index-vsu3hISM.d.ts +62 -0
  50. package/dist/chunks/limits-Ppc7BQPG.js +45 -0
  51. package/dist/chunks/limits-Ppc7BQPG.js.map +1 -0
  52. package/dist/chunks/markdown-DKJSQxR2.js +42 -0
  53. package/dist/chunks/markdown-DKJSQxR2.js.map +1 -0
  54. package/dist/chunks/mdx-components-CUUDTapN.d.ts +9 -0
  55. package/dist/chunks/normalize-DG5nxOLm.js +361 -0
  56. package/dist/chunks/normalize-DG5nxOLm.js.map +1 -0
  57. package/dist/chunks/paths-75QgA3Qs.js +16 -0
  58. package/dist/chunks/paths-75QgA3Qs.js.map +1 -0
  59. package/dist/chunks/prepare-lnuw_ycM.js +219 -0
  60. package/dist/chunks/prepare-lnuw_ycM.js.map +1 -0
  61. package/dist/chunks/read-DCv1lmcM.js +57 -0
  62. package/dist/chunks/read-DCv1lmcM.js.map +1 -0
  63. package/dist/chunks/request-DOuBHGQs.js +92 -0
  64. package/dist/chunks/request-DOuBHGQs.js.map +1 -0
  65. package/dist/chunks/response-CtzaKMWH.js +15 -0
  66. package/dist/chunks/response-CtzaKMWH.js.map +1 -0
  67. package/dist/chunks/site-DY-uZ0PP.js +419 -0
  68. package/dist/chunks/site-DY-uZ0PP.js.map +1 -0
  69. package/dist/chunks/skill-CBTGUHQH.js +85 -0
  70. package/dist/chunks/skill-CBTGUHQH.js.map +1 -0
  71. package/dist/chunks/sources-Cm6NdIen.js +236 -0
  72. package/dist/chunks/sources-Cm6NdIen.js.map +1 -0
  73. package/dist/chunks/stored-CabomV-C.js +354 -0
  74. package/dist/chunks/stored-CabomV-C.js.map +1 -0
  75. package/dist/chunks/stored-CcEfvCQH.js +475 -0
  76. package/dist/chunks/stored-CcEfvCQH.js.map +1 -0
  77. package/dist/chunks/types-DBzuXfSe.d.ts +53 -0
  78. package/dist/chunks/view-C-9ZM1zK.js +745 -0
  79. package/dist/chunks/view-C-9ZM1zK.js.map +1 -0
  80. package/dist/chunks/view-Dlf_VRI7.d.ts +151 -0
  81. package/dist/client.d.ts +73 -0
  82. package/dist/client.js +295 -0
  83. package/dist/client.js.map +1 -0
  84. package/dist/collections.d.ts +7 -0
  85. package/dist/collections.js +215 -0
  86. package/dist/collections.js.map +1 -0
  87. package/dist/entry-pages.d.ts +10 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.js +38 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/integration.d.ts +16 -0
  92. package/dist/integration.js +435 -0
  93. package/dist/integration.js.map +1 -0
  94. package/dist/llms-full.d.ts +5 -0
  95. package/dist/llms.d.ts +5 -0
  96. package/dist/markdown-alias.d.ts +7 -0
  97. package/dist/markdown.d.ts +7 -0
  98. package/dist/middleware.d.ts +8 -0
  99. package/dist/robots.d.ts +5 -0
  100. package/dist/runtime.d.ts +81 -0
  101. package/dist/runtime.js +56 -0
  102. package/dist/runtime.js.map +1 -0
  103. package/dist/scripts/restore-navigation.js +1 -0
  104. package/dist/search.d.ts +6 -0
  105. package/dist/sitemap.d.ts +5 -0
  106. package/dist/skill.d.ts +5 -0
  107. package/dist/skills.d.ts +10 -0
  108. package/dist/social.d.ts +7 -0
  109. package/dist/well-known-llms.d.ts +2 -0
  110. package/package.json +103 -3
package/dist/client.js ADDED
@@ -0,0 +1,295 @@
1
+ import { i as toSearchItems, n as readErrorData, r as toAnswerSources, t as isRateLimited } from "./chunks/limits-Ppc7BQPG.js";
2
+ import { readAnswerStream } from "@andesine/sdk/streaming";
3
+ import { Renderer, marked } from "marked";
4
+ //#region src/search/entry-pages.ts
5
+ const loaded = /* @__PURE__ */ new Map();
6
+ /** Loads the map from entry IDs to page URLs that the build writes for a source. */
7
+ const loadEntryPages = (url) => {
8
+ if (!loaded.has(url)) {
9
+ const resolver = fetch(url).then(async (response) => {
10
+ if (!response.ok) throw new Error("Search is not available now.");
11
+ const hrefs = await response.json();
12
+ return (result) => hrefs[result.entryID];
13
+ });
14
+ loaded.set(url, resolver);
15
+ resolver.catch(() => loaded.delete(url));
16
+ }
17
+ return loaded.get(url);
18
+ };
19
+ //#endregion
20
+ //#region src/search/answer-client.ts
21
+ /** The API accepts 10 history messages of up to 4,000 characters. */
22
+ const toMessages = (turns) => {
23
+ return turns.flatMap((turn) => {
24
+ return [{
25
+ role: "user",
26
+ content: turn.question.slice(0, 4e3)
27
+ }, {
28
+ role: "assistant",
29
+ content: turn.answer?.text.slice(0, 4e3) ?? ""
30
+ }];
31
+ }).filter((message) => message.content.trim()).slice(-10);
32
+ };
33
+ /** Groups answer sources by page, in order. */
34
+ const groupAnswerSources = (sources) => {
35
+ const groups = /* @__PURE__ */ new Map();
36
+ for (const source of sources) {
37
+ const page = source.href.split("#")[0];
38
+ const group = groups.get(page);
39
+ if (group) group.sources.push(source);
40
+ else groups.set(page, {
41
+ href: source.href,
42
+ sources: [source]
43
+ });
44
+ }
45
+ return [...groups.values()];
46
+ };
47
+ const readLines = async function* (body) {
48
+ const reader = body.getReader();
49
+ const decoder = new TextDecoder();
50
+ let buffer = "";
51
+ try {
52
+ while (true) {
53
+ const { done, value } = await reader.read();
54
+ if (done) break;
55
+ buffer += decoder.decode(value, { stream: true });
56
+ const lines = buffer.split("\n");
57
+ buffer = lines.pop() ?? "";
58
+ yield* lines.filter(Boolean);
59
+ }
60
+ } finally {
61
+ await reader.cancel().catch(() => {});
62
+ }
63
+ };
64
+ /** Streams the events of the site's answers route. */
65
+ const readServerEvents = async function* (context, question, history, signal) {
66
+ const response = await fetch(context.endpoint, {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/json" },
69
+ body: JSON.stringify({
70
+ source: context.source,
71
+ question,
72
+ history: toMessages(history)
73
+ }),
74
+ signal
75
+ });
76
+ if (!response.ok || !response.body) {
77
+ const body = await response.json().catch(() => ({}));
78
+ throw new Error(body.error ?? "AI answers are not available now.");
79
+ }
80
+ for await (const line of readLines(response.body)) yield JSON.parse(line);
81
+ };
82
+ /** Streams the events of the Andesine API with a publishable key, and links the cited pages. */
83
+ const readAPIEvents = async function* (api, question, history, signal) {
84
+ const [resolve, response] = await Promise.all([loadEntryPages(api.pages), fetch(`${api.url}/search/published/ask/stream`, {
85
+ method: "POST",
86
+ headers: {
87
+ "Authorization": `Bearer ${api.key}`,
88
+ "Content-Type": "application/json"
89
+ },
90
+ body: JSON.stringify({
91
+ question,
92
+ history: toMessages(history),
93
+ collectionID: api.collection,
94
+ channel: "published"
95
+ }),
96
+ signal
97
+ })]);
98
+ if (!response.ok) throw new Error(isRateLimited(response.status, await readErrorData(response)) ? "Too many questions. Try again soon." : "AI answers are not available now.");
99
+ try {
100
+ for await (const event of await readAnswerStream(response, { signal })) if (event.type === "textDelta") yield {
101
+ type: "textDelta",
102
+ text: event.text
103
+ };
104
+ else if (event.type === "sources") yield {
105
+ type: "sources",
106
+ sources: toAnswerSources(event.sources, resolve)
107
+ };
108
+ else yield {
109
+ type: "completed",
110
+ answer: event.answer,
111
+ sources: toAnswerSources(event.sources, resolve)
112
+ };
113
+ } catch (error) {
114
+ signal.throwIfAborted();
115
+ console.error(error);
116
+ throw new Error("The answer could not be completed.");
117
+ }
118
+ };
119
+ const createAnswerClient = (context) => {
120
+ return { ask: async (question, history, signal, onUpdate) => {
121
+ const events = context.api ? readAPIEvents(context.api, question, history, signal) : readServerEvents(context, question, history, signal);
122
+ let answer = {
123
+ text: "",
124
+ sources: [],
125
+ sourcesReceived: false
126
+ };
127
+ for await (const event of events) {
128
+ if (event.type === "error") throw new Error(event.error);
129
+ if (event.type === "sources") answer = {
130
+ ...answer,
131
+ sources: event.sources,
132
+ sourcesReceived: true
133
+ };
134
+ else if (event.type === "textDelta") answer = {
135
+ ...answer,
136
+ text: answer.text + event.text
137
+ };
138
+ else answer = {
139
+ text: event.answer,
140
+ sources: event.sources,
141
+ sourcesReceived: true
142
+ };
143
+ onUpdate(answer);
144
+ if (event.type === "completed") return answer;
145
+ }
146
+ signal.throwIfAborted();
147
+ throw new Error("The answer ended before completion. Try again.");
148
+ } };
149
+ };
150
+ //#endregion
151
+ //#region src/search/markdown.ts
152
+ const escapeHTML = (value) => {
153
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
154
+ };
155
+ const isRelative = (href) => href.startsWith("#") || /^\/(?!\/)/.test(href);
156
+ const isSafeLink = (href) => {
157
+ if (isRelative(href)) return true;
158
+ try {
159
+ return [
160
+ "http:",
161
+ "https:",
162
+ "mailto:"
163
+ ].includes(new URL(href).protocol);
164
+ } catch {
165
+ return false;
166
+ }
167
+ };
168
+ const renderer = new Renderer();
169
+ /** Links `[n]` references to the cited pages; references without a page stay text. */
170
+ const linkReferences = (answer) => {
171
+ const hrefs = new Map(answer.sources.map((source) => [String(source.id), source.href]));
172
+ return answer.text.replace(/\[(\d+)\](?!\()/g, (reference, id) => {
173
+ const href = hrefs.get(id);
174
+ return href ? `[${reference}](${href})` : reference;
175
+ });
176
+ };
177
+ /**
178
+ * Renders answer Markdown to HTML. Raw HTML and images become text, and only relative, web
179
+ * and mail links stay links, so the result is safe for `innerHTML`.
180
+ */
181
+ const renderAnswer = (answer) => {
182
+ return marked.parse(linkReferences(answer), {
183
+ async: false,
184
+ breaks: true,
185
+ gfm: true,
186
+ renderer
187
+ });
188
+ };
189
+ renderer.html = ({ text }) => escapeHTML(text);
190
+ renderer.image = ({ text }) => escapeHTML(text);
191
+ renderer.link = function({ href, title, tokens }) {
192
+ const label = this.parser.parseInline(tokens);
193
+ if (!isSafeLink(href)) return label;
194
+ const titleAttribute = title ? ` title="${escapeHTML(title)}"` : "";
195
+ const externalAttributes = isRelative(href) ? "" : " target=\"_blank\" rel=\"noopener noreferrer\"";
196
+ return `<a href="${escapeHTML(href)}"${titleAttribute}${externalAttributes}>${label}</a>`;
197
+ };
198
+ //#endregion
199
+ //#region src/client.ts
200
+ const limit = 8;
201
+ /** Removes the `<mark>` and other tags from Pagefind excerpts. */
202
+ const toText = (html) => {
203
+ return new DOMParser().parseFromString(html, "text/html").body.textContent ?? "";
204
+ };
205
+ const createSearchClient = (context) => {
206
+ let pagefind;
207
+ const loadPagefind = () => {
208
+ pagefind ??= import(
209
+ /* @vite-ignore */
210
+ `${context.pagefind}pagefind.js`
211
+ ).then(async (module) => {
212
+ await module.options({ baseUrl: context.pagefind.replace(/pagefind\/$/, "") });
213
+ return module;
214
+ }, (error) => {
215
+ pagefind = void 0;
216
+ throw error;
217
+ });
218
+ return pagefind;
219
+ };
220
+ const searchPagefind = async (source, query) => {
221
+ const found = await (await loadPagefind()).search(query, { filters: { source: source.id } });
222
+ return Promise.all((found?.results ?? []).slice(0, limit).map(async (result) => {
223
+ const data = await result.data();
224
+ const section = data.sub_results?.find((item) => item.url !== data.url);
225
+ return {
226
+ id: result.id,
227
+ href: section?.url ?? data.url,
228
+ title: data.meta.title ?? "Untitled",
229
+ excerpt: toText(section?.excerpt ?? data.excerpt),
230
+ headingPath: section ? [section.title] : []
231
+ };
232
+ }));
233
+ };
234
+ const searchAPI = async (api, query, signal) => {
235
+ const [resolve, response] = await Promise.all([loadEntryPages(api.pages), fetch(`${api.url}/search/published`, {
236
+ method: "POST",
237
+ headers: {
238
+ "Authorization": `Bearer ${api.key}`,
239
+ "Content-Type": "application/json"
240
+ },
241
+ body: JSON.stringify({
242
+ query,
243
+ collectionID: api.collection,
244
+ channel: "published",
245
+ limit
246
+ }),
247
+ signal
248
+ })]);
249
+ if (!response.ok) throw new Error(isRateLimited(response.status, await readErrorData(response)) ? "Too many searches. Try again soon." : "Search is not available now.");
250
+ const { results } = await response.json();
251
+ return toSearchItems(results, resolve);
252
+ };
253
+ const searchAndesine = async (source, query, signal) => {
254
+ if (source.api) return searchAPI(source.api, query, signal);
255
+ const response = await fetch(context.endpoint, {
256
+ method: "POST",
257
+ headers: { "Content-Type": "application/json" },
258
+ body: JSON.stringify({
259
+ source: source.id,
260
+ query,
261
+ limit
262
+ }),
263
+ signal
264
+ });
265
+ const body = await response.json().catch(() => ({}));
266
+ if (!response.ok) throw new Error(body.error ?? "Search is not available now.");
267
+ return body.items ?? [];
268
+ };
269
+ return { search: (query, signal) => {
270
+ return Promise.all(context.sources.map(async (source) => {
271
+ const group = {
272
+ sourceID: source.id,
273
+ label: source.label
274
+ };
275
+ try {
276
+ const items = source.type === "pagefind" ? await searchPagefind(source, query) : await searchAndesine(source, query, signal);
277
+ return {
278
+ ...group,
279
+ items
280
+ };
281
+ } catch (error) {
282
+ signal.throwIfAborted();
283
+ return {
284
+ ...group,
285
+ items: [],
286
+ error: error.message
287
+ };
288
+ }
289
+ }));
290
+ } };
291
+ };
292
+ //#endregion
293
+ export { createAnswerClient, createSearchClient, groupAnswerSources, renderAnswer };
294
+
295
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":[],"sources":["../src/search/entry-pages.ts","../src/search/answer-client.ts","../src/search/markdown.ts","../src/client.ts"],"sourcesContent":["import type { PageResolver } from \"./items\";\n\n// Each map loads once for each page view; a failed load is tried again on the next search.\nconst loaded = new Map<string, Promise<PageResolver>>();\n\n/** Loads the map from entry IDs to page URLs that the build writes for a source. */\nconst loadEntryPages = (url: string): Promise<PageResolver> => {\n if (!loaded.has(url)) {\n const resolver = fetch(url).then(async (response): Promise<PageResolver> => {\n if (!response.ok) throw new Error(\"Search is not available now.\");\n\n const hrefs = (await response.json()) as Record<string, string>;\n\n return (result) => hrefs[result.entryID];\n });\n\n loaded.set(url, resolver);\n resolver.catch(() => loaded.delete(url));\n }\n\n return loaded.get(url)!;\n};\n\nexport { loadEntryPages };\n","import { readAnswerStream } from \"@andesine/sdk/streaming\";\nimport type { AndesineAPIContext, AnswersContext } from \"../context\";\nimport { loadEntryPages } from \"./entry-pages\";\nimport { toAnswerSources } from \"./items\";\nimport { isRateLimited, readErrorData } from \"./limits\";\nimport type { Answer, AnswerEvent, AnswerMessage, AnswerSource } from \"./types\";\n\ninterface AnswerHistoryTurn {\n question: string;\n answer?: Pick<Answer, \"text\">;\n}\n\ninterface AnswerSourceGroup {\n /** The page of the first source. */\n href: string;\n sources: AnswerSource[];\n}\n\ninterface AnswerClient {\n /**\n * Streams an answer and calls `onUpdate` for each change. `history` holds the earlier turns,\n * for follow-up questions. Resolves with the completed answer; aborting the signal cancels it.\n */\n ask(\n question: string,\n history: AnswerHistoryTurn[],\n signal: AbortSignal,\n onUpdate: (answer: Answer) => void\n ): Promise<Answer>;\n}\n\n/** The API accepts 10 history messages of up to 4,000 characters. */\nconst toMessages = (turns: AnswerHistoryTurn[]): AnswerMessage[] => {\n return turns\n .flatMap((turn): AnswerMessage[] => {\n return [\n { role: \"user\", content: turn.question.slice(0, 4000) },\n { role: \"assistant\", content: turn.answer?.text.slice(0, 4000) ?? \"\" }\n ];\n })\n .filter((message) => message.content.trim())\n .slice(-10);\n};\n/** Groups answer sources by page, in order. */\nconst groupAnswerSources = (sources: AnswerSource[]): AnswerSourceGroup[] => {\n const groups = new Map<string, AnswerSourceGroup>();\n\n for (const source of sources) {\n const page = source.href.split(\"#\")[0]!;\n const group = groups.get(page);\n\n if (group) {\n group.sources.push(source);\n } else {\n groups.set(page, { href: source.href, sources: [source] });\n }\n }\n\n return [...groups.values()];\n};\nconst readLines = async function* (body: ReadableStream<Uint8Array>): AsyncGenerator<string> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n\n buffer = lines.pop() ?? \"\";\n yield* lines.filter(Boolean);\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n};\n/** Streams the events of the site's answers route. */\nconst readServerEvents = async function* (\n context: AnswersContext,\n question: string,\n history: AnswerHistoryTurn[],\n signal: AbortSignal\n): AsyncGenerator<AnswerEvent> {\n const response = await fetch(context.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ source: context.source, question, history: toMessages(history) }),\n signal\n });\n\n if (!response.ok || !response.body) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n\n throw new Error(body.error ?? \"AI answers are not available now.\");\n }\n\n for await (const line of readLines(response.body)) yield JSON.parse(line) as AnswerEvent;\n};\n/** Streams the events of the Andesine API with a publishable key, and links the cited pages. */\nconst readAPIEvents = async function* (\n api: AndesineAPIContext,\n question: string,\n history: AnswerHistoryTurn[],\n signal: AbortSignal\n): AsyncGenerator<AnswerEvent> {\n const [resolve, response] = await Promise.all([\n loadEntryPages(api.pages),\n fetch(`${api.url}/search/published/ask/stream`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${api.key}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n question,\n history: toMessages(history),\n collectionID: api.collection,\n channel: \"published\"\n }),\n signal\n })\n ]);\n\n if (!response.ok) {\n throw new Error(\n isRateLimited(response.status, await readErrorData(response))\n ? \"Too many questions. Try again soon.\"\n : \"AI answers are not available now.\"\n );\n }\n\n try {\n for await (const event of await readAnswerStream(response, { signal })) {\n if (event.type === \"textDelta\") {\n yield { type: \"textDelta\", text: event.text };\n } else if (event.type === \"sources\") {\n yield { type: \"sources\", sources: toAnswerSources(event.sources, resolve) };\n } else {\n yield {\n type: \"completed\",\n answer: event.answer,\n sources: toAnswerSources(event.sources, resolve)\n };\n }\n }\n } catch (error) {\n signal.throwIfAborted();\n console.error(error);\n throw new Error(\"The answer could not be completed.\");\n }\n};\nconst createAnswerClient = (context: AnswersContext): AnswerClient => {\n return {\n ask: async (question, history, signal, onUpdate) => {\n const events = context.api\n ? readAPIEvents(context.api, question, history, signal)\n : readServerEvents(context, question, history, signal);\n\n let answer: Answer = { text: \"\", sources: [], sourcesReceived: false };\n\n for await (const event of events) {\n if (event.type === \"error\") throw new Error(event.error);\n\n if (event.type === \"sources\") {\n answer = { ...answer, sources: event.sources, sourcesReceived: true };\n } else if (event.type === \"textDelta\") {\n answer = { ...answer, text: answer.text + event.text };\n } else {\n answer = { text: event.answer, sources: event.sources, sourcesReceived: true };\n }\n\n onUpdate(answer);\n\n if (event.type === \"completed\") return answer;\n }\n\n signal.throwIfAborted();\n throw new Error(\"The answer ended before completion. Try again.\");\n }\n };\n};\n\nexport { createAnswerClient, groupAnswerSources };\nexport type { AnswerClient, AnswerHistoryTurn, AnswerSourceGroup };\n","import { marked, Renderer, type Tokens } from \"marked\";\nimport type { Answer } from \"./types\";\n\nconst escapeHTML = (value: string): string => {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#39;\");\n};\nconst isRelative = (href: string): boolean => href.startsWith(\"#\") || /^\\/(?!\\/)/.test(href);\nconst isSafeLink = (href: string): boolean => {\n if (isRelative(href)) return true;\n\n try {\n return [\"http:\", \"https:\", \"mailto:\"].includes(new URL(href).protocol);\n } catch {\n return false;\n }\n};\nconst renderer = new Renderer();\n/** Links `[n]` references to the cited pages; references without a page stay text. */\nconst linkReferences = (answer: Answer): string => {\n const hrefs = new Map(answer.sources.map((source) => [String(source.id), source.href]));\n\n return answer.text.replace(/\\[(\\d+)\\](?!\\()/g, (reference, id: string) => {\n const href = hrefs.get(id);\n\n return href ? `[${reference}](${href})` : reference;\n });\n};\n/**\n * Renders answer Markdown to HTML. Raw HTML and images become text, and only relative, web\n * and mail links stay links, so the result is safe for `innerHTML`.\n */\nconst renderAnswer = (answer: Answer): string => {\n return marked.parse(linkReferences(answer), { async: false, breaks: true, gfm: true, renderer });\n};\n\nrenderer.html = ({ text }: Tokens.HTML | Tokens.Tag): string => escapeHTML(text);\nrenderer.image = ({ text }: Tokens.Image): string => escapeHTML(text);\nrenderer.link = function ({ href, title, tokens }: Tokens.Link): string {\n const label = this.parser.parseInline(tokens);\n\n if (!isSafeLink(href)) return label;\n\n const titleAttribute = title ? ` title=\"${escapeHTML(title)}\"` : \"\";\n const externalAttributes = isRelative(href) ? \"\" : ' target=\"_blank\" rel=\"noopener noreferrer\"';\n\n return `<a href=\"${escapeHTML(href)}\"${titleAttribute}${externalAttributes}>${label}</a>`;\n};\n\nexport { renderAnswer };\n","import type { AndesineAPIContext, SearchContext, SearchSourceContext } from \"./context\";\nimport { loadEntryPages } from \"./search/entry-pages\";\nimport { toSearchItems } from \"./search/items\";\nimport { isRateLimited, readErrorData } from \"./search/limits\";\nimport type { SearchGroup, SearchItem } from \"./search/types\";\n\ninterface SearchClient {\n /** Searches all sources. Aborting the signal cancels the pending requests. */\n search(query: string, signal: AbortSignal): Promise<SearchGroup[]>;\n}\n\ninterface Pagefind {\n options(options: { baseUrl: string }): Promise<void>;\n search(\n query: string,\n options: { filters: Record<string, string> }\n ): Promise<{ results: Array<{ id: string; data(): Promise<PagefindResultData> }> } | null>;\n}\n\ninterface PagefindResultData {\n url: string;\n excerpt: string;\n meta: { title?: string };\n sub_results?: Array<{ url: string; title: string; excerpt: string }>;\n}\n\nconst limit = 8;\n\n/** Removes the `<mark>` and other tags from Pagefind excerpts. */\nconst toText = (html: string): string => {\n return new DOMParser().parseFromString(html, \"text/html\").body.textContent ?? \"\";\n};\nconst createSearchClient = (context: SearchContext): SearchClient => {\n let pagefind: Promise<Pagefind> | undefined;\n\n const loadPagefind = (): Promise<Pagefind> => {\n pagefind ??= import(/* @vite-ignore */ `${context.pagefind}pagefind.js`).then(\n async (module: Pagefind) => {\n await module.options({ baseUrl: context.pagefind.replace(/pagefind\\/$/, \"\") });\n\n return module;\n },\n (error) => {\n pagefind = undefined;\n throw error;\n }\n );\n\n return pagefind;\n };\n const searchPagefind = async (\n source: SearchSourceContext,\n query: string\n ): Promise<SearchItem[]> => {\n const found = await (await loadPagefind()).search(query, { filters: { source: source.id } });\n\n return Promise.all(\n (found?.results ?? []).slice(0, limit).map(async (result) => {\n const data = await result.data();\n // The first sub-result is the best matching section of the page.\n const section = data.sub_results?.find((item) => item.url !== data.url);\n\n return {\n id: result.id,\n href: section?.url ?? data.url,\n title: data.meta.title ?? \"Untitled\",\n excerpt: toText(section?.excerpt ?? data.excerpt),\n headingPath: section ? [section.title] : []\n };\n })\n );\n };\n // With a publishable key, the browser searches through the API, and links results itself.\n const searchAPI = async (\n api: AndesineAPIContext,\n query: string,\n signal: AbortSignal\n ): Promise<SearchItem[]> => {\n const [resolve, response] = await Promise.all([\n loadEntryPages(api.pages),\n fetch(`${api.url}/search/published`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${api.key}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ query, collectionID: api.collection, channel: \"published\", limit }),\n signal\n })\n ]);\n\n if (!response.ok) {\n throw new Error(\n isRateLimited(response.status, await readErrorData(response))\n ? \"Too many searches. Try again soon.\"\n : \"Search is not available now.\"\n );\n }\n\n const { results } = (await response.json()) as { results: Parameters<typeof toSearchItems>[0] };\n\n return toSearchItems(results, resolve);\n };\n const searchAndesine = async (\n source: SearchSourceContext,\n query: string,\n signal: AbortSignal\n ): Promise<SearchItem[]> => {\n if (source.api) return searchAPI(source.api, query, signal);\n\n const response = await fetch(context.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ source: source.id, query, limit }),\n signal\n });\n const body = (await response.json().catch(() => ({}))) as {\n items?: SearchItem[];\n error?: string;\n };\n\n if (!response.ok) throw new Error(body.error ?? \"Search is not available now.\");\n\n return body.items ?? [];\n };\n\n return {\n search: (query, signal) => {\n return Promise.all(\n context.sources.map(async (source): Promise<SearchGroup> => {\n const group = { sourceID: source.id, label: source.label };\n\n try {\n const items =\n source.type === \"pagefind\"\n ? await searchPagefind(source, query)\n : await searchAndesine(source, query, signal);\n\n return { ...group, items };\n } catch (error) {\n signal.throwIfAborted();\n\n return { ...group, items: [], error: (error as Error).message };\n }\n })\n );\n }\n };\n};\n\nexport { createSearchClient };\nexport { createAnswerClient, groupAnswerSources } from \"./search/answer-client\";\nexport { renderAnswer } from \"./search/markdown\";\nexport type { SearchClient };\nexport type { AnswerClient, AnswerHistoryTurn, AnswerSourceGroup } from \"./search/answer-client\";\nexport type { SearchGroup, SearchItem, Answer, AnswerSource, AnswerMessage } from \"./search/types\";\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ //#region src/collections.d.ts
2
+ declare const collections: {
3
+ [k: string]: any;
4
+ };
5
+ //#endregion
6
+ export { collections };
7
+ //# sourceMappingURL=collections.d.ts.map
@@ -0,0 +1,215 @@
1
+ import { a as mimeTypes, i as getDeliveryURL, r as getAssetPrefix } from "./chunks/assets-D6WJmZ05.js";
2
+ import { t as toSHA256 } from "./chunks/hash-D4piIIQ6.js";
3
+ import { n as recordID, r as createAndesinePages } from "./chunks/stored-CabomV-C.js";
4
+ import { n as readPublication } from "./chunks/read-DCv1lmcM.js";
5
+ import { n as normalizeSpec } from "./chunks/normalize-DG5nxOLm.js";
6
+ import { r as recordID$1 } from "./chunks/stored-CcEfvCQH.js";
7
+ import { existsSync } from "node:fs";
8
+ import { resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import { mkdir, writeFile } from "node:fs/promises";
11
+ import config, { highlight } from "virtual:andesine/config";
12
+ import { defineCollection } from "astro/content/config";
13
+ import { glob } from "astro/loaders";
14
+ import { z } from "astro/zod";
15
+ import { bundle } from "@scalar/json-magic/bundle";
16
+ import { fetchUrls, parseJson, parseYaml, readFiles } from "@scalar/json-magic/bundle/plugins/node";
17
+ import { validate } from "@scalar/openapi-parser";
18
+ //#region src/sources/andesine/asset-cache.ts
19
+ /**
20
+ * Downloads images into a cache directory with content-hash names. The build copies them to
21
+ * the output, so pages do not depend on expiring delivery URLs.
22
+ */
23
+ const createAssetCache = (directory, base) => {
24
+ const prefix = getAssetPrefix(base);
25
+ const files = /* @__PURE__ */ new Set();
26
+ const downloads = /* @__PURE__ */ new Map();
27
+ const download = async (asset, entryID) => {
28
+ const label = `Entry ${entryID}, asset ${asset.assetID}`;
29
+ const response = await fetch(getDeliveryURL(asset, entryID), {
30
+ credentials: "omit",
31
+ signal: AbortSignal.timeout(3e4)
32
+ });
33
+ const type = response.headers.get("content-type")?.split(";")[0];
34
+ if (!response.ok) throw new Error(`${label}: download failed (${response.status}).`);
35
+ if (type !== mimeTypes[asset.format]) throw new Error(`${label}: unexpected type ${type}.`);
36
+ const bytes = new Uint8Array(await response.arrayBuffer());
37
+ const file = `${await toSHA256(bytes)}.${asset.format}`;
38
+ await mkdir(directory, { recursive: true });
39
+ await writeFile(new URL(file, directory), bytes);
40
+ files.add(file);
41
+ return `${prefix}${file}`;
42
+ };
43
+ return {
44
+ resolve: (asset, entryID) => {
45
+ const key = `${asset.assetID}:${asset.variant}`;
46
+ if (!downloads.has(key)) downloads.set(key, download(asset, entryID));
47
+ return downloads.get(key);
48
+ },
49
+ finish: async () => {
50
+ await mkdir(directory, { recursive: true });
51
+ await writeFile(new URL("files.json", directory), JSON.stringify([...files]));
52
+ }
53
+ };
54
+ };
55
+ //#endregion
56
+ //#region src/sources/andesine/collection.ts
57
+ /** Reads the latest publication at build time, and copies its images into the build. */
58
+ const createAndesineCollection = (source, base, site, highlight) => {
59
+ return defineCollection({ loader: {
60
+ name: `andesine-${source.id}`,
61
+ load: async ({ store, config, logger }) => {
62
+ const assets = createAssetCache(new URL(`andesine/${source.id}/`, config.cacheDir), base);
63
+ const { tree, entries } = await readPublication(source);
64
+ const pages = await createAndesinePages(source, tree.collection, entries, {
65
+ base,
66
+ site,
67
+ assets,
68
+ highlight
69
+ });
70
+ const stored = {
71
+ tree: tree.collection,
72
+ pages
73
+ };
74
+ await assets.finish();
75
+ store.clear();
76
+ store.set({
77
+ id: recordID,
78
+ data: stored
79
+ });
80
+ logger.info(`Source "${source.id}": loaded ${pages.length} published pages.`);
81
+ }
82
+ } });
83
+ };
84
+ //#endregion
85
+ //#region src/sources/files/schema.ts
86
+ const fileSchema = z.object({
87
+ title: z.string().trim().min(1),
88
+ description: z.string().trim().min(1).optional(),
89
+ /** Replaces the path from the file location, relative to the source mount. */
90
+ slug: z.string().trim().optional(),
91
+ navigationLabel: z.string().trim().min(1).optional(),
92
+ navigationHidden: z.boolean().default(false),
93
+ searchHidden: z.boolean().default(false),
94
+ /** Lower numbers come first. Items with the same order sort by path. */
95
+ order: z.number().default(0),
96
+ toc: z.boolean().default(true),
97
+ layout: z.enum(["docs", "wide"]).default("docs"),
98
+ /** The date of the last change, e.g. `2026-09-27`, for the page footer and the sitemap. */
99
+ updatedAt: z.coerce.date().optional()
100
+ }).strict();
101
+ //#endregion
102
+ //#region src/sources/files/collection.ts
103
+ const createFilesCollection = (source) => {
104
+ return defineCollection({
105
+ loader: glob({
106
+ base: source.directory,
107
+ pattern: "**/*.{md,mdx}",
108
+ generateId: ({ entry }) => entry
109
+ }),
110
+ schema: fileSchema
111
+ });
112
+ };
113
+ //#endregion
114
+ //#region src/sources/openapi/read.ts
115
+ const toErrorLine = (error) => {
116
+ const path = Array.isArray(error.path) ? error.path.join("/") : error.path;
117
+ return `- ${path ? `${path}: ` : ""}${error.message}`;
118
+ };
119
+ /** Reads local files, like `readFiles`, and adds their paths to `files`. */
120
+ const readTrackedFiles = (files) => {
121
+ const plugin = readFiles();
122
+ return {
123
+ ...plugin,
124
+ exec: (value) => {
125
+ files.add(resolve(value));
126
+ return plugin.exec(value);
127
+ }
128
+ };
129
+ };
130
+ /**
131
+ * Reads a spec with its external references into one document, and validates it. The spec is a
132
+ * file relative to `root`, or an HTTPS URL.
133
+ */
134
+ const readSpec = async (source, root) => {
135
+ const label = `Source "${source.id}"`;
136
+ const isRemote = source.spec.startsWith("https://");
137
+ const input = isRemote ? source.spec : fileURLToPath(new URL(source.spec, root));
138
+ const files = new Set(isRemote ? [] : [input]);
139
+ if (!isRemote && !existsSync(input)) throw new Error(`${label}: cannot find ${source.spec}. Use a path from the project root or an HTTPS URL.`);
140
+ const document = await bundle(input, {
141
+ plugins: [
142
+ readTrackedFiles(files),
143
+ fetchUrls(),
144
+ parseJson(),
145
+ parseYaml()
146
+ ],
147
+ treeShake: false
148
+ }).catch((error) => {
149
+ throw new Error(`${label}: cannot read ${source.spec}. ${error.message}`);
150
+ });
151
+ const version = String(document.openapi ?? document.swagger ?? "");
152
+ if (!/^3\.[01]\./.test(version)) throw new Error(`${label}: OpenAPI ${version || "without a version"} is not supported. Use 3.0 or 3.1.`);
153
+ const result = await validate(document);
154
+ if (!result.valid) {
155
+ const errors = (result.errors ?? []).slice(0, 10).map(toErrorLine);
156
+ throw new Error(`${label}: ${source.spec} is not a valid OpenAPI document.\n${errors.join("\n")}`);
157
+ }
158
+ return {
159
+ document,
160
+ files: [...files]
161
+ };
162
+ };
163
+ //#endregion
164
+ //#region src/sources/openapi/collection.ts
165
+ /**
166
+ * Reads and normalizes a spec at build time. A local spec loads again when it, or a local file
167
+ * that it references, changes.
168
+ */
169
+ const createOpenAPICollection = (source) => {
170
+ const watchers = /* @__PURE__ */ new WeakSet();
171
+ let reload;
172
+ let files = [];
173
+ let reloads = Promise.resolve();
174
+ return defineCollection({ loader: {
175
+ name: `openapi-${source.id}`,
176
+ load: async ({ store, config, logger, watcher }) => {
177
+ const load = async () => {
178
+ const spec = await readSpec(source, config.root);
179
+ const specURL = source.spec.startsWith("https://") ? source.spec : void 0;
180
+ const { model, ignored } = normalizeSpec(spec.document, `Source "${source.id}"`, specURL);
181
+ const stored = { model };
182
+ files = spec.files;
183
+ watcher?.add(files);
184
+ store.clear();
185
+ store.set({
186
+ id: recordID$1,
187
+ data: stored
188
+ });
189
+ logger.info(`Source "${source.id}": loaded ${model.operations.length} operations.`);
190
+ if (ignored.length) logger.warn(`Source "${source.id}": ${ignored.join(" and ")} are not shown.`);
191
+ };
192
+ reload = load;
193
+ await load();
194
+ if (!watcher || watchers.has(watcher)) return;
195
+ const onChange = (changed) => {
196
+ if (!files.includes(changed)) return;
197
+ reloads = reloads.then(() => reload?.()).catch((error) => logger.error(error.message));
198
+ };
199
+ watchers.add(watcher);
200
+ watcher.on("add", onChange);
201
+ watcher.on("change", onChange);
202
+ }
203
+ } });
204
+ };
205
+ //#endregion
206
+ //#region src/collections.ts
207
+ const collections = Object.fromEntries(config.sources.flatMap((source) => {
208
+ if (source.type === "files") return [[source.id, createFilesCollection(source)]];
209
+ if (source.type === "openapi") return [[source.id, createOpenAPICollection(source)]];
210
+ return [[source.id, createAndesineCollection(source, config.base, config.site, highlight)]];
211
+ }));
212
+ //#endregion
213
+ export { collections };
214
+
215
+ //# sourceMappingURL=collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections.js","names":["recordID"],"sources":["../src/sources/andesine/asset-cache.ts","../src/sources/andesine/collection.ts","../src/sources/files/schema.ts","../src/sources/files/collection.ts","../src/sources/openapi/read.ts","../src/sources/openapi/collection.ts","../src/collections.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport type { PublishedAsset } from \"@andesine/sdk\";\nimport { toSHA256 } from \"../../hash\";\nimport { type AssetStore, getAssetPrefix, getDeliveryURL, mimeTypes } from \"./assets\";\n\ninterface AssetCache extends AssetStore {\n /** Lists the stored files, for the build output. */\n finish(): Promise<void>;\n}\n\n/**\n * Downloads images into a cache directory with content-hash names. The build copies them to\n * the output, so pages do not depend on expiring delivery URLs.\n */\nconst createAssetCache = (directory: URL, base: string): AssetCache => {\n const prefix = getAssetPrefix(base);\n const files = new Set<string>();\n const downloads = new Map<string, Promise<string>>();\n const download = async (asset: PublishedAsset, entryID: string): Promise<string> => {\n const label = `Entry ${entryID}, asset ${asset.assetID}`;\n const response = await fetch(getDeliveryURL(asset, entryID), {\n credentials: \"omit\",\n signal: AbortSignal.timeout(30_000)\n });\n const type = response.headers.get(\"content-type\")?.split(\";\")[0];\n\n if (!response.ok) throw new Error(`${label}: download failed (${response.status}).`);\n if (type !== mimeTypes[asset.format]) throw new Error(`${label}: unexpected type ${type}.`);\n\n const bytes = new Uint8Array(await response.arrayBuffer());\n const file = `${await toSHA256(bytes)}.${asset.format}`;\n\n await mkdir(directory, { recursive: true });\n await writeFile(new URL(file, directory), bytes);\n files.add(file);\n\n return `${prefix}${file}`;\n };\n\n return {\n resolve: (asset, entryID) => {\n const key = `${asset.assetID}:${asset.variant}`;\n\n if (!downloads.has(key)) downloads.set(key, download(asset, entryID));\n\n return downloads.get(key)!;\n },\n finish: async () => {\n await mkdir(directory, { recursive: true });\n await writeFile(new URL(\"files.json\", directory), JSON.stringify([...files]));\n }\n };\n};\n\nexport { createAssetCache };\nexport type { AssetCache };\n","import { defineCollection } from \"astro/content/config\";\nimport type { AndesineSourceConfig } from \"../../config\";\nimport type { HighlightOptions } from \"../../content/prepare\";\nimport { createAssetCache } from \"./asset-cache\";\nimport { createAndesinePages } from \"./load\";\nimport { readPublication } from \"./read\";\nimport { recordID, type StoredSource } from \"./stored\";\n\n/** Reads the latest publication at build time, and copies its images into the build. */\nconst createAndesineCollection = (\n source: AndesineSourceConfig,\n base: string,\n site: string,\n highlight: HighlightOptions\n) => {\n return defineCollection({\n loader: {\n name: `andesine-${source.id}`,\n load: async ({ store, config, logger }) => {\n const assets = createAssetCache(new URL(`andesine/${source.id}/`, config.cacheDir), base);\n const { tree, entries } = await readPublication(source);\n const pages = await createAndesinePages(source, tree.collection, entries, {\n base,\n site,\n assets,\n highlight\n });\n const stored: StoredSource = { tree: tree.collection, pages };\n\n await assets.finish();\n store.clear();\n store.set({ id: recordID, data: stored as never });\n logger.info(`Source \"${source.id}\": loaded ${pages.length} published pages.`);\n }\n }\n });\n};\n\nexport { createAndesineCollection };\n","import { z } from \"astro/zod\";\n\nconst fileSchema = z\n .object({\n title: z.string().trim().min(1),\n description: z.string().trim().min(1).optional(),\n /** Replaces the path from the file location, relative to the source mount. */\n slug: z.string().trim().optional(),\n navigationLabel: z.string().trim().min(1).optional(),\n navigationHidden: z.boolean().default(false),\n searchHidden: z.boolean().default(false),\n /** Lower numbers come first. Items with the same order sort by path. */\n order: z.number().default(0),\n toc: z.boolean().default(true),\n layout: z.enum([\"docs\", \"wide\"]).default(\"docs\"),\n /** The date of the last change, e.g. `2026-09-27`, for the page footer and the sitemap. */\n updatedAt: z.coerce.date().optional()\n })\n .strict();\n\ntype FileMetadata = z.output<typeof fileSchema>;\n\nexport { fileSchema };\nexport type { FileMetadata };\n","import { defineCollection } from \"astro/content/config\";\nimport { glob } from \"astro/loaders\";\nimport type { FilesSourceConfig } from \"../../config\";\nimport { fileSchema } from \"./schema\";\n\nconst createFilesCollection = (source: FilesSourceConfig) => {\n return defineCollection({\n // Keep extensions in IDs, so `a.md` and `a.mdx` cannot replace each other.\n loader: glob({\n base: source.directory,\n pattern: \"**/*.{md,mdx}\",\n generateId: ({ entry }) => entry\n }),\n schema: fileSchema\n });\n};\n\nexport { createFilesCollection };\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { bundle } from \"@scalar/json-magic/bundle\";\nimport { fetchUrls, parseJson, parseYaml, readFiles } from \"@scalar/json-magic/bundle/plugins/node\";\nimport { validate, type ErrorObject } from \"@scalar/openapi-parser\";\nimport type { OpenAPISourceConfig } from \"../../config\";\n\ninterface ReadSpec {\n document: Record<string, unknown>;\n /** The local files of the spec and its references, for watching. */\n files: string[];\n}\n\nconst toErrorLine = (error: ErrorObject): string => {\n const path = Array.isArray(error.path) ? error.path.join(\"/\") : error.path;\n\n return `- ${path ? `${path}: ` : \"\"}${error.message}`;\n};\n/** Reads local files, like `readFiles`, and adds their paths to `files`. */\nconst readTrackedFiles = (files: Set<string>): ReturnType<typeof readFiles> => {\n const plugin = readFiles();\n\n return {\n ...plugin,\n exec: (value) => {\n files.add(resolve(value));\n\n return plugin.exec(value);\n }\n };\n};\n/**\n * Reads a spec with its external references into one document, and validates it. The spec is a\n * file relative to `root`, or an HTTPS URL.\n */\nconst readSpec = async (source: OpenAPISourceConfig, root: URL): Promise<ReadSpec> => {\n const label = `Source \"${source.id}\"`;\n const isRemote = source.spec.startsWith(\"https://\");\n const input = isRemote ? source.spec : fileURLToPath(new URL(source.spec, root));\n const files = new Set(isRemote ? [] : [input]);\n\n if (!isRemote && !existsSync(input)) {\n throw new Error(\n `${label}: cannot find ${source.spec}. Use a path from the project root or an HTTPS URL.`\n );\n }\n\n const document = (await bundle(input, {\n plugins: [readTrackedFiles(files), fetchUrls(), parseJson(), parseYaml()],\n treeShake: false\n }).catch((error: Error) => {\n throw new Error(`${label}: cannot read ${source.spec}. ${error.message}`);\n })) as Record<string, unknown>;\n const version = String(document.openapi ?? document.swagger ?? \"\");\n\n if (!/^3\\.[01]\\./.test(version)) {\n throw new Error(\n `${label}: OpenAPI ${version || \"without a version\"} is not supported. Use 3.0 or 3.1.`\n );\n }\n\n const result = await validate(document);\n\n if (!result.valid) {\n const errors = (result.errors ?? []).slice(0, 10).map(toErrorLine);\n\n throw new Error(\n `${label}: ${source.spec} is not a valid OpenAPI document.\\n${errors.join(\"\\n\")}`\n );\n }\n\n return { document, files: [...files] };\n};\n\nexport { readSpec };\nexport type { ReadSpec };\n","import { defineCollection } from \"astro/content/config\";\nimport type { OpenAPISourceConfig } from \"../../config\";\nimport { normalizeSpec } from \"./normalize\";\nimport { readSpec } from \"./read\";\nimport { recordID, type StoredSpec } from \"./stored\";\n\n/**\n * Reads and normalizes a spec at build time. A local spec loads again when it, or a local file\n * that it references, changes.\n */\nconst createOpenAPICollection = (source: OpenAPISourceConfig) => {\n // Syncs call the loader again; the watcher calls the latest load, and gets one listener.\n const watchers = new WeakSet<object>();\n\n let reload: (() => Promise<void>) | undefined;\n let files: string[] = [];\n // Reloads run in order, so a slower, older reload cannot replace a newer model.\n let reloads = Promise.resolve();\n\n return defineCollection({\n loader: {\n name: `openapi-${source.id}`,\n load: async ({ store, config, logger, watcher }) => {\n const load = async (): Promise<void> => {\n const spec = await readSpec(source, config.root);\n const specURL = source.spec.startsWith(\"https://\") ? source.spec : undefined;\n const { model, ignored } = normalizeSpec(spec.document, `Source \"${source.id}\"`, specURL);\n const stored: StoredSpec = { model };\n\n // References can change, so the new files are watched too.\n files = spec.files;\n watcher?.add(files);\n\n store.clear();\n store.set({ id: recordID, data: stored as never });\n logger.info(`Source \"${source.id}\": loaded ${model.operations.length} operations.`);\n\n if (ignored.length) {\n logger.warn(`Source \"${source.id}\": ${ignored.join(\" and \")} are not shown.`);\n }\n };\n\n reload = load;\n await load();\n\n if (!watcher || watchers.has(watcher)) return;\n\n const onChange = (changed: string): void => {\n if (!files.includes(changed)) return;\n\n reloads = reloads\n .then(() => reload?.())\n .catch((error: Error) => logger.error(error.message));\n };\n\n watchers.add(watcher);\n // A referenced file that did not exist yet is added, not changed.\n watcher.on(\"add\", onChange);\n watcher.on(\"change\", onChange);\n }\n }\n });\n};\n\nexport { createOpenAPICollection };\n","import config, { highlight } from \"virtual:andesine/config\";\nimport { createAndesineCollection } from \"./sources/andesine/collection\";\nimport { createFilesCollection } from \"./sources/files/collection\";\nimport { createOpenAPICollection } from \"./sources/openapi/collection\";\n\nconst collections = Object.fromEntries(\n config.sources.flatMap((source) => {\n if (source.type === \"files\") return [[source.id, createFilesCollection(source)]];\n if (source.type === \"openapi\") return [[source.id, createOpenAPICollection(source)]];\n\n return [[source.id, createAndesineCollection(source, config.base, config.site, highlight)]];\n })\n);\n\nexport { collections };\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { APIRoute } from "astro";
2
+ //#region src/astro/routes/entry-pages.d.ts
3
+ export declare const getStaticPaths: () => any;
4
+ /**
5
+ * Maps the entry IDs of a source with a publishable key to page URLs, so the browser can link
6
+ * API search results and answer sources. Pages hidden from search are left out.
7
+ */
8
+ export declare const GET: APIRoute;
9
+ //#endregion
10
+ //# sourceMappingURL=entry-pages.d.ts.map
@@ -0,0 +1,5 @@
1
+ import { t as FileTreeItem } from "./chunks/mdx-components-CUUDTapN.js";
2
+ import { A as FilesSourceConfig, C as SiteContext, D as parseConfig, E as defineConfig, F as PagesConfigInput, I as SectionConfig, L as SourceConfig, M as LinkConfig, N as OpenAPISourceConfig, O as AIService, P as PagesConfig, R as configSchema, S as SectionContext, T as SocialImage, _ as PageFragments, b as SearchContext, c as createSiteContext, d as BreadcrumbItem, f as HeadingContext, g as PageContext, h as PageActionsContext, j as IconLinkConfig, k as AndesineSourceConfig, l as AndesineAPIContext, m as OpenInLink, n as createPageContext, p as NavigationItem, r as toSectionContexts, t as createPageMeta, u as AnswersContext, v as PageLink, w as SiteLogo, x as SearchSourceContext, y as PageMeta } from "./chunks/index-D8wL2B5s.js";
3
+ import { a as RequestBodyView, c as ServerView, d as FieldView, f as MediaView, h as Fact, i as ParameterGroupView, l as ViewOptions, m as toID, n as OperationParts, o as ResponseView, p as VariantView, r as OperationView, s as SecuritySchemeView, t as CodeView, u as createOperationView } from "./chunks/view-Dlf_VRI7.js";
4
+ import { a as MarkupValue, i as Markup, n as SocialCardData, o as html, r as SocialCardFont, t as SocialCard } from "./chunks/types-DBzuXfSe.js";
5
+ export { type AIService, type AndesineAPIContext, type AndesineSourceConfig, type AnswersContext, type BreadcrumbItem, type CodeView, type Fact, type FieldView, type FileTreeItem, type FilesSourceConfig, type HeadingContext, type IconLinkConfig, type LinkConfig, type Markup, type MarkupValue, type MediaView, type NavigationItem, type OpenAPISourceConfig, type OpenInLink, type OperationParts, type OperationView, type PageActionsContext, type PageContext, type PageFragments, type PageLink, type PageMeta, type PagesConfig, type PagesConfigInput, type ParameterGroupView, type RequestBodyView, type ResponseView, type SearchContext, type SearchSourceContext, type SectionConfig, type SectionContext, type SecuritySchemeView, type ServerView, type SiteContext, type SiteLogo, type SocialCard, type SocialCardData, type SocialCardFont, type SocialImage, type SourceConfig, type VariantView, type ViewOptions, type configSchema, type createOperationView, type createPageContext, type createPageMeta, type createSiteContext, defineConfig, html, type parseConfig, type toID, type toSectionContexts };
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import { t as defineConfig } from "./chunks/config-CuH5JSI4.js";
2
+ //#region src/social/html.ts
3
+ const entities = {
4
+ "&": "&amp;",
5
+ "<": "&lt;",
6
+ ">": "&gt;",
7
+ "\"": "&quot;"
8
+ };
9
+ /** Card markup from the `html` template tag, whose values are already escaped. */
10
+ var Markup = class {
11
+ value;
12
+ constructor(value) {
13
+ this.value = value;
14
+ }
15
+ toString() {
16
+ return this.value;
17
+ }
18
+ };
19
+ const toMarkup = (value) => {
20
+ const isEmpty = value === null || value === void 0 || typeof value === "boolean";
21
+ if (value instanceof Markup) return value.value;
22
+ if (Array.isArray(value)) return value.map(toMarkup).join("");
23
+ if (isEmpty) return "";
24
+ return String(value).replace(/[&<>"]/g, (character) => entities[character]);
25
+ };
26
+ /**
27
+ * A template tag for social card markup. It escapes values, joins arrays, and leaves out
28
+ * `false`, `null`, and `undefined`, so `${condition && html`…`}` adds optional parts.
29
+ */
30
+ const html = (strings, ...values) => {
31
+ return new Markup(strings.reduce((result, part, index) => {
32
+ return result + part + (index < values.length ? toMarkup(values[index]) : "");
33
+ }, ""));
34
+ };
35
+ //#endregion
36
+ export { defineConfig, html };
37
+
38
+ //# sourceMappingURL=index.js.map