@andesine/pages 0.0.0-stage → 0.4.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 +99 -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
@@ -0,0 +1,184 @@
1
+ import { z } from "zod";
2
+ //#region src/config/schema.ts
3
+ const identifierSchema = z.string().regex(/^[a-z][\w-]*$/, "Use lowercase letters, digits, - or _.");
4
+ const iconSchema = z.string().regex(/^i-[\w-]+:[\w-]+$/, "Use an icon class, e.g. i-lucide:book.");
5
+ const pathSchema = z.string().regex(/^\/(?:.*\/)?$/, "Use a path that starts and ends with /.");
6
+ const baseSchema = pathSchema.refine((path) => path.split("/").every((segment) => encodeURIComponent(segment) === segment), "Use a base path with letters, digits, -, _, ., !, ~, *, ', ( and ) only.");
7
+ const linkSchema = z.object({
8
+ label: z.string().min(1),
9
+ href: z.string().min(1),
10
+ icon: iconSchema.optional()
11
+ }).strict();
12
+ const iconLinkSchema = z.object({
13
+ label: z.string().min(1),
14
+ href: z.string().min(1),
15
+ icon: iconSchema
16
+ }).strict();
17
+ const logoSchema = z.object({
18
+ src: z.string().min(1),
19
+ /** A square mark next to the site name, or a full wordmark before it. */
20
+ format: z.enum(["icon", "full"]).default("icon"),
21
+ /** The text in a full logo, for screen readers, e.g. "Andesine". */
22
+ alt: z.string().min(1).optional(),
23
+ /** Shows the site name after the logo. */
24
+ title: z.boolean().default(true)
25
+ }).strict();
26
+ const brandSchema = z.object({
27
+ primary: z.string().min(1).default("#ff3617"),
28
+ secondary: z.string().min(1).default("#f88f52"),
29
+ tertiary: z.string().min(1).default("#fc6335")
30
+ }).strict();
31
+ const pageActionsSchema = z.object({
32
+ /** AI chat services that open with the page's Markdown as context. */
33
+ openIn: z.array(z.enum([
34
+ "chatgpt",
35
+ "claude",
36
+ "perplexity"
37
+ ])).default(["chatgpt", "claude"]) }).strict();
38
+ const agentsSchema = z.object({
39
+ /** Site-wide guidance for AI agents, in the Markdown pages, `llms.txt`, and `llms-full.txt`. */
40
+ instructions: z.string().trim().min(1).optional(),
41
+ /** Path to your own `SKILL.md`, relative to the config. It replaces the generated skill. */
42
+ skill: z.string().regex(/^\.\.?\//, "Use a path relative to the config, e.g. ./SKILL.md.").optional()
43
+ }).strict();
44
+ const socialSchema = z.object({
45
+ /** The image for link previews when pages have no generated image. */
46
+ image: z.string().min(1).optional(),
47
+ /** Generates an image for each page with the template's social card. */
48
+ generate: z.boolean().default(true),
49
+ /** Background image of generated cards. */
50
+ background: z.string().min(1).optional()
51
+ }).strict();
52
+ const sectionSchema = z.object({
53
+ id: identifierSchema,
54
+ label: z.string().min(1),
55
+ icon: iconSchema.optional(),
56
+ sources: z.array(identifierSchema).min(1),
57
+ /** Links at the top of the navigation on this section's pages, after the site's links. */
58
+ links: z.array(iconLinkSchema).default([])
59
+ }).strict();
60
+ const filesSourceSchema = z.object({
61
+ id: identifierSchema,
62
+ type: z.literal("files"),
63
+ directory: z.string().min(1),
64
+ mount: pathSchema.default("/")
65
+ }).strict();
66
+ const andesineSourceSchema = z.object({
67
+ id: identifierSchema,
68
+ type: z.literal("andesine"),
69
+ /** ID of the published collection. The build reads its latest publication. */
70
+ collection: z.string().min(1),
71
+ mount: pathSchema.default("/"),
72
+ apiURL: z.url({ protocol: /^https?$/ }).optional(),
73
+ /**
74
+ * Name of the environment variable with the secret API key, which is never in config.
75
+ * Optional with `publicKey`; the build then reads with the publishable key.
76
+ */
77
+ apiKeyEnv: z.string().min(1).default("ANDESINE_API_KEY"),
78
+ /** AI answers in search. The API key needs the `ai-answers` permission. */
79
+ answers: z.boolean().default(true),
80
+ /**
81
+ * A publishable key (`adn_pk_...`), which is safe in browsers. With it, search and answers
82
+ * call the Andesine API directly, so they need no server, and the build needs no secret key.
83
+ */
84
+ publicKey: z.string().regex(/^adn_pk_[A-Za-z\d]+$/, "Use a publishable key (adn_pk_...), never a secret key.").optional()
85
+ }).strict();
86
+ const openAPISourceSchema = z.object({
87
+ id: identifierSchema,
88
+ type: z.literal("openapi"),
89
+ /** An OpenAPI 3.0 or 3.1 file, relative to the project root, or an HTTPS URL. */
90
+ spec: z.string().min(1),
91
+ mount: pathSchema.default("/"),
92
+ /** Makes a landing page for each tag with a description. Without it, tags only group pages. */
93
+ tagPages: z.boolean().default(true)
94
+ }).strict();
95
+ const sourceSchema = z.discriminatedUnion("type", [
96
+ filesSourceSchema,
97
+ andesineSourceSchema,
98
+ openAPISourceSchema
99
+ ]);
100
+ const configSchema = z.object({
101
+ name: z.string().min(1),
102
+ description: z.string().optional(),
103
+ site: z.url({ protocol: /^https?$/ }),
104
+ base: baseSchema.default("/"),
105
+ language: z.string().min(2).default("en"),
106
+ logo: logoSchema.optional(),
107
+ favicon: z.string().min(1).optional(),
108
+ brand: brandSchema.prefault({}),
109
+ /** Icon links at the top of the navigation, e.g. to a changelog or a community. */
110
+ links: z.array(iconLinkSchema).default([]),
111
+ cta: linkSchema.optional(),
112
+ /** Icon links in the footer, e.g. to GitHub or Discord. */
113
+ socialLinks: z.array(iconLinkSchema).default([]),
114
+ sections: z.array(sectionSchema).default([]),
115
+ /** Shows sections as tabs in the header, or as links at the top of the navigation. */
116
+ sectionsDisplay: z.enum(["tabs", "navigation"]).default("tabs"),
117
+ sources: z.array(sourceSchema).default([]),
118
+ /** The page actions menu; `false` hides it. Markdown pages exist either way. */
119
+ pageActions: z.union([z.literal(false), pageActionsSchema]).prefault({}),
120
+ agents: agentsSchema.prefault({}),
121
+ social: socialSchema.prefault({})
122
+ }).strict().superRefine((config, context) => {
123
+ const sourceIDs = /* @__PURE__ */ new Set();
124
+ const sectionSources = /* @__PURE__ */ new Map();
125
+ config.sources.forEach((source, index) => {
126
+ if (sourceIDs.has(source.id)) context.addIssue({
127
+ code: "custom",
128
+ path: [
129
+ "sources",
130
+ index,
131
+ "id"
132
+ ],
133
+ message: `Source ID "${source.id}" is not unique.`
134
+ });
135
+ sourceIDs.add(source.id);
136
+ });
137
+ config.sections.forEach((section, index) => {
138
+ section.sources.forEach((sourceID, sourceIndex) => {
139
+ const path = [
140
+ "sections",
141
+ index,
142
+ "sources",
143
+ sourceIndex
144
+ ];
145
+ const owner = sectionSources.get(sourceID);
146
+ if (!sourceIDs.has(sourceID)) context.addIssue({
147
+ code: "custom",
148
+ path,
149
+ message: `Source "${sourceID}" does not exist.`
150
+ });
151
+ if (owner) context.addIssue({
152
+ code: "custom",
153
+ path,
154
+ message: `Source "${sourceID}" is already in section "${owner}".`
155
+ });
156
+ sectionSources.set(sourceID, section.id);
157
+ });
158
+ });
159
+ if (!config.sections.length) return;
160
+ config.sources.forEach((source, index) => {
161
+ if (sectionSources.has(source.id)) return;
162
+ context.addIssue({
163
+ code: "custom",
164
+ path: [
165
+ "sources",
166
+ index,
167
+ "id"
168
+ ],
169
+ message: `Source "${source.id}" is not in a section.`
170
+ });
171
+ });
172
+ });
173
+ //#endregion
174
+ //#region src/config/index.ts
175
+ const parseConfig = (input, source = "Andesine Pages config") => {
176
+ const result = configSchema.safeParse(input);
177
+ if (!result.success) throw new Error(`Invalid ${source}:\n${z.prettifyError(result.error)}`);
178
+ return result.data;
179
+ };
180
+ const defineConfig = (input) => parseConfig(input);
181
+ //#endregion
182
+ export { parseConfig as n, defineConfig as t };
183
+
184
+ //# sourceMappingURL=config-CuH5JSI4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-CuH5JSI4.js","names":[],"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\nconst identifierSchema = z\n .string()\n .regex(/^[a-z][\\w-]*$/, \"Use lowercase letters, digits, - or _.\");\nconst iconSchema = z.string().regex(/^i-[\\w-]+:[\\w-]+$/, \"Use an icon class, e.g. i-lucide:book.\");\nconst pathSchema = z.string().regex(/^\\/(?:.*\\/)?$/, \"Use a path that starts and ends with /.\");\n// Base-prefixed URLs join the base as is, so it cannot need encoding.\nconst baseSchema = pathSchema.refine(\n (path) => path.split(\"/\").every((segment) => encodeURIComponent(segment) === segment),\n \"Use a base path with letters, digits, -, _, ., !, ~, *, ', ( and ) only.\"\n);\nconst linkSchema = z\n .object({ label: z.string().min(1), href: z.string().min(1), icon: iconSchema.optional() })\n .strict();\nconst iconLinkSchema = z\n .object({ label: z.string().min(1), href: z.string().min(1), icon: iconSchema })\n .strict();\nconst logoSchema = z\n .object({\n src: z.string().min(1),\n /** A square mark next to the site name, or a full wordmark before it. */\n format: z.enum([\"icon\", \"full\"]).default(\"icon\"),\n /** The text in a full logo, for screen readers, e.g. \"Andesine\". */\n alt: z.string().min(1).optional(),\n /** Shows the site name after the logo. */\n title: z.boolean().default(true)\n })\n .strict();\nconst brandSchema = z\n .object({\n primary: z.string().min(1).default(\"#ff3617\"),\n secondary: z.string().min(1).default(\"#f88f52\"),\n tertiary: z.string().min(1).default(\"#fc6335\")\n })\n .strict();\nconst pageActionsSchema = z\n .object({\n /** AI chat services that open with the page's Markdown as context. */\n openIn: z.array(z.enum([\"chatgpt\", \"claude\", \"perplexity\"])).default([\"chatgpt\", \"claude\"])\n })\n .strict();\nconst agentsSchema = z\n .object({\n /** Site-wide guidance for AI agents, in the Markdown pages, `llms.txt`, and `llms-full.txt`. */\n instructions: z.string().trim().min(1).optional(),\n /** Path to your own `SKILL.md`, relative to the config. It replaces the generated skill. */\n skill: z\n .string()\n .regex(/^\\.\\.?\\//, \"Use a path relative to the config, e.g. ./SKILL.md.\")\n .optional()\n })\n .strict();\nconst socialSchema = z\n .object({\n /** The image for link previews when pages have no generated image. */\n image: z.string().min(1).optional(),\n /** Generates an image for each page with the template's social card. */\n generate: z.boolean().default(true),\n /** Background image of generated cards. */\n background: z.string().min(1).optional()\n })\n .strict();\nconst sectionSchema = z\n .object({\n id: identifierSchema,\n label: z.string().min(1),\n icon: iconSchema.optional(),\n sources: z.array(identifierSchema).min(1),\n /** Links at the top of the navigation on this section's pages, after the site's links. */\n links: z.array(iconLinkSchema).default([])\n })\n .strict();\nconst filesSourceSchema = z\n .object({\n id: identifierSchema,\n type: z.literal(\"files\"),\n directory: z.string().min(1),\n mount: pathSchema.default(\"/\")\n })\n .strict();\nconst andesineSourceSchema = z\n .object({\n id: identifierSchema,\n type: z.literal(\"andesine\"),\n /** ID of the published collection. The build reads its latest publication. */\n collection: z.string().min(1),\n mount: pathSchema.default(\"/\"),\n apiURL: z.url({ protocol: /^https?$/ }).optional(),\n /**\n * Name of the environment variable with the secret API key, which is never in config.\n * Optional with `publicKey`; the build then reads with the publishable key.\n */\n apiKeyEnv: z.string().min(1).default(\"ANDESINE_API_KEY\"),\n /** AI answers in search. The API key needs the `ai-answers` permission. */\n answers: z.boolean().default(true),\n /**\n * A publishable key (`adn_pk_...`), which is safe in browsers. With it, search and answers\n * call the Andesine API directly, so they need no server, and the build needs no secret key.\n */\n publicKey: z\n .string()\n .regex(/^adn_pk_[A-Za-z\\d]+$/, \"Use a publishable key (adn_pk_...), never a secret key.\")\n .optional()\n })\n .strict();\nconst openAPISourceSchema = z\n .object({\n id: identifierSchema,\n type: z.literal(\"openapi\"),\n /** An OpenAPI 3.0 or 3.1 file, relative to the project root, or an HTTPS URL. */\n spec: z.string().min(1),\n mount: pathSchema.default(\"/\"),\n /** Makes a landing page for each tag with a description. Without it, tags only group pages. */\n tagPages: z.boolean().default(true)\n })\n .strict();\nconst sourceSchema = z.discriminatedUnion(\"type\", [\n filesSourceSchema,\n andesineSourceSchema,\n openAPISourceSchema\n]);\nconst configSchema = z\n .object({\n name: z.string().min(1),\n description: z.string().optional(),\n site: z.url({ protocol: /^https?$/ }),\n base: baseSchema.default(\"/\"),\n language: z.string().min(2).default(\"en\"),\n logo: logoSchema.optional(),\n favicon: z.string().min(1).optional(),\n brand: brandSchema.prefault({}),\n /** Icon links at the top of the navigation, e.g. to a changelog or a community. */\n links: z.array(iconLinkSchema).default([]),\n cta: linkSchema.optional(),\n /** Icon links in the footer, e.g. to GitHub or Discord. */\n socialLinks: z.array(iconLinkSchema).default([]),\n sections: z.array(sectionSchema).default([]),\n /** Shows sections as tabs in the header, or as links at the top of the navigation. */\n sectionsDisplay: z.enum([\"tabs\", \"navigation\"]).default(\"tabs\"),\n sources: z.array(sourceSchema).default([]),\n /** The page actions menu; `false` hides it. Markdown pages exist either way. */\n pageActions: z.union([z.literal(false), pageActionsSchema]).prefault({}),\n agents: agentsSchema.prefault({}),\n social: socialSchema.prefault({})\n })\n .strict()\n .superRefine((config, context) => {\n const sourceIDs = new Set<string>();\n const sectionSources = new Map<string, string>();\n\n config.sources.forEach((source, index) => {\n if (sourceIDs.has(source.id)) {\n context.addIssue({\n code: \"custom\",\n path: [\"sources\", index, \"id\"],\n message: `Source ID \"${source.id}\" is not unique.`\n });\n }\n\n sourceIDs.add(source.id);\n });\n\n config.sections.forEach((section, index) => {\n section.sources.forEach((sourceID, sourceIndex) => {\n const path = [\"sections\", index, \"sources\", sourceIndex];\n const owner = sectionSources.get(sourceID);\n\n if (!sourceIDs.has(sourceID)) {\n context.addIssue({\n code: \"custom\",\n path,\n message: `Source \"${sourceID}\" does not exist.`\n });\n }\n\n if (owner) {\n context.addIssue({\n code: \"custom\",\n path,\n message: `Source \"${sourceID}\" is already in section \"${owner}\".`\n });\n }\n\n sectionSources.set(sourceID, section.id);\n });\n });\n\n if (!config.sections.length) return;\n\n config.sources.forEach((source, index) => {\n if (sectionSources.has(source.id)) return;\n\n context.addIssue({\n code: \"custom\",\n path: [\"sources\", index, \"id\"],\n message: `Source \"${source.id}\" is not in a section.`\n });\n });\n });\n\ntype PagesConfigInput = z.input<typeof configSchema>;\ntype PagesConfig = z.output<typeof configSchema>;\ntype SourceConfig = PagesConfig[\"sources\"][number];\ntype FilesSourceConfig = Extract<SourceConfig, { type: \"files\" }>;\ntype AndesineSourceConfig = Extract<SourceConfig, { type: \"andesine\" }>;\ntype OpenAPISourceConfig = Extract<SourceConfig, { type: \"openapi\" }>;\ntype SectionConfig = PagesConfig[\"sections\"][number];\ntype LinkConfig = z.output<typeof linkSchema>;\ntype IconLinkConfig = PagesConfig[\"links\"][number];\ntype AIService = z.output<typeof pageActionsSchema>[\"openIn\"][number];\n\nexport { configSchema };\nexport type {\n PagesConfigInput,\n PagesConfig,\n SourceConfig,\n FilesSourceConfig,\n AndesineSourceConfig,\n OpenAPISourceConfig,\n SectionConfig,\n LinkConfig,\n IconLinkConfig,\n AIService\n};\n","import { z } from \"zod\";\nimport { configSchema, type PagesConfig, type PagesConfigInput } from \"./schema\";\n\nconst parseConfig = (input: unknown, source = \"Andesine Pages config\"): PagesConfig => {\n const result = configSchema.safeParse(input);\n\n if (!result.success) {\n throw new Error(`Invalid ${source}:\\n${z.prettifyError(result.error)}`);\n }\n\n return result.data;\n};\nconst defineConfig = (input: PagesConfigInput): PagesConfig => parseConfig(input);\n\nexport { defineConfig, parseConfig };\nexport type * from \"./schema\";\n"],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { i as toLine, n as createPageBody, r as toLabel } from "./markdown-DKJSQxR2.js";
2
+ //#region src/output/files.ts
3
+ const escapeXML = (value) => {
4
+ return value.replace(/[&<>"]/g, (character) => `&#${character.charCodeAt(0)};`);
5
+ };
6
+ const createSitemap = (config, catalog) => {
7
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${catalog.pages.map((page) => {
8
+ return `<url>${`<loc>${escapeXML(new URL(page.href, config.site).href)}</loc>`}${page.updatedAt ? `<lastmod>${page.updatedAt}</lastmod>` : ""}</url>`;
9
+ }).join("")}</urlset>\n`;
10
+ };
11
+ /** The site title, description, and agent instructions, which start both LLM files. */
12
+ const createIntroduction = (config) => {
13
+ return [
14
+ `# ${toLine(config.name)}`,
15
+ ...config.description ? [`> ${toLine(config.description)}`] : [],
16
+ ...config.agents.instructions ? [config.agents.instructions] : []
17
+ ];
18
+ };
19
+ /** Lists the Markdown alternatives by section, after https://llmstxt.org. */
20
+ const createLLMs = (config, catalog) => {
21
+ const sections = catalog.sections.filter((section) => section.pages.length).map((section) => {
22
+ const links = section.pages.map((page) => {
23
+ const url = new URL(`${page.href}index.md`, config.site).href;
24
+ const description = page.description ? `: ${toLine(page.description)}` : "";
25
+ return `- [${toLabel(page.title)}](${url})${description}`;
26
+ });
27
+ return `## ${toLine(section.label)}\n\n${links.join("\n")}`;
28
+ });
29
+ const sectionLinks = config.sections.flatMap((section) => section.links);
30
+ const links = [
31
+ ...config.links,
32
+ ...sectionLinks,
33
+ ...config.cta ? [config.cta] : []
34
+ ].map((link) => `- [${toLabel(link.label)}](${new URL(link.href, config.site).href})`);
35
+ const optional = links.length ? [`## Optional\n\n${[...new Set(links)].join("\n")}`] : [];
36
+ return `${[
37
+ ...createIntroduction(config),
38
+ ...sections,
39
+ ...optional
40
+ ].join("\n\n")}\n`;
41
+ };
42
+ /** All pages in one file, in navigation order. */
43
+ const createLLMsFull = (config, catalog) => {
44
+ const pages = [...new Set(catalog.sections.flatMap((section) => section.pages))].map((page) => {
45
+ return createPageBody(page, new URL(page.href, config.site).href);
46
+ });
47
+ return `${[...createIntroduction(config), ...pages].join("\n\n")}\n`;
48
+ };
49
+ /** Crawlers read `robots.txt` only at the domain root, so it has an effect only for `base: "/"`. */
50
+ const createRobots = (config) => {
51
+ return [
52
+ "User-agent: *",
53
+ `Allow: ${config.base}`,
54
+ `Disallow: ${config.base}_andesine/`,
55
+ `Sitemap: ${new URL(`${config.base}sitemap.xml`, config.site).href}`,
56
+ ""
57
+ ].join("\n");
58
+ };
59
+ //#endregion
60
+ export { createSitemap as i, createLLMsFull as n, createRobots as r, createLLMs as t };
61
+
62
+ //# sourceMappingURL=files-BxqiQzFj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files-BxqiQzFj.js","names":[],"sources":["../../src/output/files.ts"],"sourcesContent":["import type { PagesConfig } from \"../config\";\nimport type { Catalog } from \"./catalog\";\nimport { createPageBody, toLabel, toLine } from \"./markdown\";\n\nconst escapeXML = (value: string): string => {\n return value.replace(/[&<>\"]/g, (character) => `&#${character.charCodeAt(0)};`);\n};\nconst createSitemap = (config: PagesConfig, catalog: Catalog): string => {\n const urls = catalog.pages.map((page) => {\n const location = `<loc>${escapeXML(new URL(page.href, config.site).href)}</loc>`;\n const modified = page.updatedAt ? `<lastmod>${page.updatedAt}</lastmod>` : \"\";\n\n return `<url>${location}${modified}</url>`;\n });\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">${urls.join(\"\")}</urlset>\\n`;\n};\n/** The site title, description, and agent instructions, which start both LLM files. */\nconst createIntroduction = (config: PagesConfig): string[] => {\n return [\n `# ${toLine(config.name)}`,\n ...(config.description ? [`> ${toLine(config.description)}`] : []),\n ...(config.agents.instructions ? [config.agents.instructions] : [])\n ];\n};\n/** Lists the Markdown alternatives by section, after https://llmstxt.org. */\nconst createLLMs = (config: PagesConfig, catalog: Catalog): string => {\n const sections = catalog.sections\n .filter((section) => section.pages.length)\n .map((section) => {\n const links = section.pages.map((page) => {\n const url = new URL(`${page.href}index.md`, config.site).href;\n const description = page.description ? `: ${toLine(page.description)}` : \"\";\n\n return `- [${toLabel(page.title)}](${url})${description}`;\n });\n\n return `## ${toLine(section.label)}\\n\\n${links.join(\"\\n\")}`;\n });\n const sectionLinks = config.sections.flatMap((section) => section.links);\n const links = [...config.links, ...sectionLinks, ...(config.cta ? [config.cta] : [])].map(\n (link) => `- [${toLabel(link.label)}](${new URL(link.href, config.site).href})`\n );\n // The llms.txt format marks links that agents can skip as \"Optional\".\n const optional = links.length ? [`## Optional\\n\\n${[...new Set(links)].join(\"\\n\")}`] : [];\n\n return `${[...createIntroduction(config), ...sections, ...optional].join(\"\\n\\n\")}\\n`;\n};\n/** All pages in one file, in navigation order. */\nconst createLLMsFull = (config: PagesConfig, catalog: Catalog): string => {\n const pages = [...new Set(catalog.sections.flatMap((section) => section.pages))].map((page) => {\n return createPageBody(page, new URL(page.href, config.site).href);\n });\n\n return `${[...createIntroduction(config), ...pages].join(\"\\n\\n\")}\\n`;\n};\n/** Crawlers read `robots.txt` only at the domain root, so it has an effect only for `base: \"/\"`. */\nconst createRobots = (config: PagesConfig): string => {\n return [\n \"User-agent: *\",\n `Allow: ${config.base}`,\n `Disallow: ${config.base}_andesine/`,\n `Sitemap: ${new URL(`${config.base}sitemap.xml`, config.site).href}`,\n \"\"\n ].join(\"\\n\");\n};\n\nexport { createSitemap, createLLMs, createLLMsFull, createRobots };\n"],"mappings":";;AAIA,MAAM,aAAa,UAA0B;CAC3C,OAAO,MAAM,QAAQ,YAAY,cAAc,KAAK,UAAU,WAAW,CAAC,EAAE,EAAE;AAChF;AACA,MAAM,iBAAiB,QAAqB,YAA6B;CAQvE,OAAO,uGAPM,QAAQ,MAAM,KAAK,SAAS;EAIvC,OAAO,QAAQ,QAHU,UAAU,IAAI,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,IAAI,EAAE,UACxD,KAAK,YAAY,YAAY,KAAK,UAAU,cAAc,GAExC;CACrC,CAEiH,CAAC,CAAC,KAAK,EAAE,EAAE;AAC9H;;AAEA,MAAM,sBAAsB,WAAkC;CAC5D,OAAO;EACL,KAAK,OAAO,OAAO,IAAI;EACvB,GAAI,OAAO,cAAc,CAAC,KAAK,OAAO,OAAO,WAAW,GAAG,IAAI,CAAC;EAChE,GAAI,OAAO,OAAO,eAAe,CAAC,OAAO,OAAO,YAAY,IAAI,CAAC;CACnE;AACF;;AAEA,MAAM,cAAc,QAAqB,YAA6B;CACpE,MAAM,WAAW,QAAQ,SACtB,QAAQ,YAAY,QAAQ,MAAM,MAAM,CAAC,CACzC,KAAK,YAAY;EAChB,MAAM,QAAQ,QAAQ,MAAM,KAAK,SAAS;GACxC,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,KAAK,WAAW,OAAO,IAAI,CAAC,CAAC;GACzD,MAAM,cAAc,KAAK,cAAc,KAAK,OAAO,KAAK,WAAW,MAAM;GAEzE,OAAO,MAAM,QAAQ,KAAK,KAAK,EAAE,IAAI,IAAI,GAAG;EAC9C,CAAC;EAED,OAAO,MAAM,OAAO,QAAQ,KAAK,EAAE,MAAM,MAAM,KAAK,IAAI;CAC1D,CAAC;CACH,MAAM,eAAe,OAAO,SAAS,SAAS,YAAY,QAAQ,KAAK;CACvE,MAAM,QAAQ;EAAC,GAAG,OAAO;EAAO,GAAG;EAAc,GAAI,OAAO,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CAAE,CAAC,CAAC,KACnF,SAAS,MAAM,QAAQ,KAAK,KAAK,EAAE,IAAI,IAAI,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,KAAK,EAC/E;CAEA,MAAM,WAAW,MAAM,SAAS,CAAC,kBAAkB,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC;CAExF,OAAO,GAAG;EAAC,GAAG,mBAAmB,MAAM;EAAG,GAAG;EAAU,GAAG;CAAQ,CAAC,CAAC,KAAK,MAAM,EAAE;AACnF;;AAEA,MAAM,kBAAkB,QAAqB,YAA6B;CACxE,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,QAAQ,SAAS,SAAS,YAAY,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS;EAC7F,OAAO,eAAe,MAAM,IAAI,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,IAAI;CAClE,CAAC;CAED,OAAO,GAAG,CAAC,GAAG,mBAAmB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,KAAK,MAAM,EAAE;AACnE;;AAEA,MAAM,gBAAgB,WAAgC;CACpD,OAAO;EACL;EACA,UAAU,OAAO;EACjB,aAAa,OAAO,KAAK;EACzB,YAAY,IAAI,IAAI,GAAG,OAAO,KAAK,cAAc,OAAO,IAAI,CAAC,CAAC;EAC9D;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
@@ -0,0 +1,11 @@
1
+ //#region src/hash.ts
2
+ /** Returns the SHA-256 digest as hex. Web Crypto works in every server runtime. */
3
+ const toSHA256 = async (data) => {
4
+ const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
5
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
6
+ return Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
7
+ };
8
+ //#endregion
9
+ export { toSHA256 as t };
10
+
11
+ //# sourceMappingURL=hash-D4piIIQ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-D4piIIQ6.js","names":[],"sources":["../../src/hash.ts"],"sourcesContent":["/** Returns the SHA-256 digest as hex. Web Crypto works in every server runtime. */\nconst toSHA256 = async (data: string | Uint8Array<ArrayBuffer>): Promise<string> => {\n const bytes = typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n const digest = new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes));\n\n return Array.from(digest, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n};\n\nexport { toSHA256 };\n"],"mappings":";;AACA,MAAM,WAAW,OAAO,SAA4D;CAClF,MAAM,QAAQ,OAAO,SAAS,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,IAAI;CAC1E,MAAM,SAAS,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK,CAAC;CAE1E,OAAO,MAAM,KAAK,SAAS,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AACjF"}