blume 1.5.2 → 1.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.
- package/CHANGELOG.md +85 -0
- package/dist/cli/index.js +3639 -1377
- package/dist/cli/index.js.map +103 -91
- package/dist/types/ai/component-markdown.d.ts +79 -0
- package/dist/types/components/layout/nav-utils.d.ts +60 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +206 -4
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +23 -1
- package/dist/types/core/github.d.ts +35 -0
- package/dist/types/core/i18n-ui.d.ts +8 -0
- package/dist/types/core/navigation.d.ts +69 -0
- package/dist/types/core/schema.d.ts +117 -1
- package/dist/types/core/sources/types.d.ts +31 -6
- package/dist/types/core/types.d.ts +23 -2
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/openapi/references.d.ts +21 -1
- package/dist/types/seo/jsonld.d.ts +105 -0
- package/dist/types/theme/fonts.d.ts +34 -4
- package/docs/_snippets/include-demo.mdx +7 -0
- package/docs/advanced/api-reference.mdx +3 -3
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/graphql.mdx +84 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/configuration/ai.mdx +21 -3
- package/docs/configuration/index.mdx +24 -0
- package/docs/configuration/search.mdx +13 -1
- package/docs/configuration/seo.mdx +27 -0
- package/docs/configuration/theming.mdx +17 -0
- package/docs/content/components.mdx +7 -0
- package/docs/content/includes.mdx +68 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +25 -0
- package/docs/content/sources.mdx +42 -1
- package/docs/content/syntax.mdx +69 -1
- package/docs/content/versioning.mdx +15 -9
- package/docs/reference/cli.mdx +2 -1
- package/package.json +23 -14
- package/skills/blume-migrate/SKILL.md +16 -7
- package/skills/blume-migrate/references/docusaurus.md +5 -3
- package/skills/blume-migrate/references/fumadocs.md +10 -2
- package/skills/blume-migrate/references/mintlify.md +3 -2
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +1 -1
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/ask-data.ts +2 -1
- package/src/ai/component-markdown.ts +199 -36
- package/src/ai/llms.ts +93 -6
- package/src/ai/markdown.ts +2 -2
- package/src/ai/mcp/discovery.ts +10 -2
- package/src/ai/mcp/server.ts +74 -2
- package/src/astro/generate.ts +183 -116
- package/src/astro/include-hmr.ts +81 -0
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +3 -5
- package/src/astro/templates.ts +125 -76
- package/src/cli/commands/build.ts +84 -15
- package/src/cli/init/questions.ts +1 -0
- package/src/cli/init/scaffold.ts +27 -4
- package/src/components/colors.ts +142 -0
- package/src/components/content/Badge.astro +5 -12
- package/src/components/content/Callout.astro +19 -36
- package/src/components/content/Card.astro +15 -21
- package/src/components/content/Component.astro +10 -1
- package/src/components/content/GithubInfo.astro +28 -9
- package/src/components/content/Tabs.astro +27 -5
- package/src/components/content/github-info.ts +20 -5
- package/src/components/dropdown-dismiss.ts +122 -0
- package/src/components/layout/Fonts.astro +15 -8
- package/src/components/layout/Header.astro +44 -0
- package/src/components/layout/LanguageSwitcher.astro +9 -1
- package/src/components/layout/NavSelector.astro +12 -3
- package/src/components/layout/NavTree.astro +6 -18
- package/src/components/layout/PageActions.astro +29 -8
- package/src/components/layout/PageLayout.astro +10 -1
- package/src/components/layout/ReferenceLayout.astro +6 -1
- package/src/components/layout/RootLayout.astro +46 -15
- package/src/components/layout/Search.astro +36 -4
- package/src/components/layout/TableOfContents.astro +8 -2
- package/src/components/layout/head-scripts.ts +53 -1
- package/src/components/openapi/ApiOverview.astro +13 -3
- package/src/components/openapi/AsyncApiOperation.astro +7 -14
- package/src/components/openapi/GraphqlChip.astro +33 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +111 -0
- package/src/components/openapi/GraphqlOperation.astro +186 -0
- package/src/components/openapi/GraphqlType.astro +154 -0
- package/src/components/openapi/MethodBadge.astro +3 -14
- package/src/components/openapi/Operation.astro +12 -5
- package/src/components/openapi/OperationPanel.astro +43 -0
- package/src/components/openapi/RequestPanel.astro +5 -10
- package/src/components/openapi/Responses.astro +1 -16
- package/src/components/openapi/graphql-helpers.ts +466 -0
- package/src/components/openapi/playground-client.ts +15 -0
- package/src/components/openapi/sample-panels.ts +45 -0
- package/src/components/openapi/snippets.ts +13 -35
- package/src/core/base-path.ts +11 -0
- package/src/core/config-input.ts +209 -2
- package/src/core/config.ts +6 -4
- package/src/core/content-assets.ts +15 -4
- package/src/core/data.ts +18 -2
- package/src/core/diagnostics.ts +8 -0
- package/src/core/frontmatter.ts +20 -8
- package/src/core/github.ts +71 -0
- package/src/core/graph.ts +22 -8
- package/src/core/heading-markers.ts +96 -0
- package/src/core/i18n-ui.ts +11 -0
- package/src/core/includes.ts +632 -0
- package/src/core/last-modified.ts +36 -11
- package/src/core/links.ts +79 -13
- package/src/core/meta.ts +2 -1
- package/src/core/nav-diagnostics.ts +11 -2
- package/src/core/navigation.ts +27 -6
- package/src/core/project-graph.ts +61 -9
- package/src/core/schema.ts +226 -35
- package/src/core/server-features.ts +5 -9
- package/src/core/sources/github-releases.ts +2 -2
- package/src/core/sources/normalize.ts +502 -115
- package/src/core/sources/notion.ts +43 -8
- package/src/core/sources/obsidian.ts +1038 -0
- package/src/core/sources/read.ts +36 -1
- package/src/core/sources/resolve.ts +34 -1
- package/src/core/sources/types.ts +28 -6
- package/src/core/sources/watch.ts +12 -8
- package/src/core/tsconfig-aliases.ts +48 -35
- package/src/core/types.ts +25 -2
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +2 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +2 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +2 -0
- package/src/core/ui-packs/el.ts +2 -0
- package/src/core/ui-packs/es.ts +2 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +2 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +2 -0
- package/src/core/ui-packs/hu.ts +2 -0
- package/src/core/ui-packs/id.ts +2 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +2 -0
- package/src/core/ui-packs/ko.ts +2 -0
- package/src/core/ui-packs/nl.ts +2 -0
- package/src/core/ui-packs/no.ts +2 -0
- package/src/core/ui-packs/pl.ts +2 -0
- package/src/core/ui-packs/pt-br.ts +2 -0
- package/src/core/ui-packs/pt.ts +2 -0
- package/src/core/ui-packs/ro.ts +2 -0
- package/src/core/ui-packs/ru.ts +2 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +2 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +2 -0
- package/src/core/ui-packs/uk.ts +2 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/core/version-cut.ts +21 -3
- package/src/core/yaml.ts +26 -0
- package/src/deploy/function-bundle.ts +251 -0
- package/src/eval/schema.ts +3 -1
- package/src/markdown/code-title.ts +22 -16
- package/src/markdown/features.ts +21 -0
- package/src/markdown/fence-meta.ts +50 -0
- package/src/markdown/heading-anchors.ts +198 -37
- package/src/markdown/include.ts +247 -0
- package/src/markdown/index.ts +43 -34
- package/src/markdown/language-icon.ts +2 -2
- package/src/markdown/mdast.ts +7 -3
- package/src/markdown/ts2js.ts +264 -0
- package/src/openapi/asyncapi.ts +4 -1
- package/src/openapi/graphql-build.ts +293 -0
- package/src/openapi/graphql.ts +212 -0
- package/src/openapi/model.ts +38 -5
- package/src/openapi/parse.ts +34 -0
- package/src/openapi/proxy.ts +30 -5
- package/src/openapi/references.ts +89 -13
- package/src/openapi/render-mdx.ts +48 -8
- package/src/openapi/scalar.ts +5 -12
- package/src/openapi/source.ts +91 -23
- package/src/registry/eject.ts +11 -0
- package/src/search/documents.ts +229 -37
- package/src/search/orama-index.ts +9 -5
- package/src/seo/jsonld.ts +293 -51
- package/src/theme/code-block-padding.ts +16 -0
- package/src/theme/entry.ts +65 -11
- package/src/theme/fonts.ts +189 -16
- package/src/translate/prompts.ts +2 -0
- package/src/translate/run.ts +7 -0
- package/src/translate/work-list.ts +0 -0
package/src/core/schema.ts
CHANGED
|
@@ -7,11 +7,21 @@ import { normalizeRoute } from "../openapi/references.ts";
|
|
|
7
7
|
import { normalizeXHandle } from "../seo/x-handle.ts";
|
|
8
8
|
import { FONT_SLUGS, isFontSlug } from "../theme/fonts.ts";
|
|
9
9
|
import { normalizeBasePath } from "./base-path.ts";
|
|
10
|
+
import { PUBLIC_HOST_URL } from "./github.ts";
|
|
10
11
|
import { uiLocaleOverridesSchema } from "./i18n-ui.ts";
|
|
11
12
|
import { openInChatProviders } from "./open-in-chat.ts";
|
|
12
13
|
import type { ContentSource } from "./sources/types.ts";
|
|
13
14
|
import { isStandardSchema } from "./standard-schema.ts";
|
|
14
15
|
import type { StandardSchema } from "./standard-schema.ts";
|
|
16
|
+
import { trimEnd } from "./trim.ts";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An absolute HTTP(S) URL, for any field that lands verbatim in an `href` —
|
|
20
|
+
* the header repo mark, the GitHub instance origin. Zod's bare `z.url()` also
|
|
21
|
+
* admits `javascript:` and `data:`, which would render as a script link on
|
|
22
|
+
* every page.
|
|
23
|
+
*/
|
|
24
|
+
const httpUrlSchema = z.url({ protocol: /^https?$/u });
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
27
|
* Public Blume schemas.
|
|
@@ -347,6 +357,21 @@ const notionSourceSchema = z.object({
|
|
|
347
357
|
type: z.literal("notion"),
|
|
348
358
|
});
|
|
349
359
|
|
|
360
|
+
/**
|
|
361
|
+
* An Obsidian vault, read in place. Wikilinks become route links and
|
|
362
|
+
* `%%comments%%` are stripped at load time, so the vault stays the source of
|
|
363
|
+
* truth — no export step and no generated notes in the repo.
|
|
364
|
+
*/
|
|
365
|
+
const obsidianSourceSchema = z.strictObject({
|
|
366
|
+
/** Vault folder names to skip at any depth, in addition to dot-folders. */
|
|
367
|
+
exclude: z.array(z.string()).optional(),
|
|
368
|
+
/** Namespaces the source's routes under `/<prefix>/`; e.g. `vault`. */
|
|
369
|
+
prefix: z.string().optional(),
|
|
370
|
+
type: z.literal("obsidian"),
|
|
371
|
+
/** Vault directory, absolute or relative to the project root. */
|
|
372
|
+
vault: z.string().min(1),
|
|
373
|
+
});
|
|
374
|
+
|
|
350
375
|
/**
|
|
351
376
|
* A repo's GitHub Releases, materialized as `type: changelog` entries — release
|
|
352
377
|
* notes become the changelog with no files to maintain. A private repo reads a
|
|
@@ -396,6 +421,7 @@ const contentSourceSchema = z.discriminatedUnion("type", [
|
|
|
396
421
|
githubReleasesSourceSchema,
|
|
397
422
|
sanitySourceSchema,
|
|
398
423
|
notionSourceSchema,
|
|
424
|
+
obsidianSourceSchema,
|
|
399
425
|
customSourceSchema,
|
|
400
426
|
]);
|
|
401
427
|
|
|
@@ -547,6 +573,8 @@ const remoteFontSchema = z.strictObject({
|
|
|
547
573
|
provider: z
|
|
548
574
|
.enum(["google", "fontsource", "bunny", "fontshare"])
|
|
549
575
|
.default("google"),
|
|
576
|
+
/** Character subsets to load; defaults to `latin` plus the locales' scripts. */
|
|
577
|
+
subsets: z.array(z.string().min(1)).nonempty().optional(),
|
|
550
578
|
weights: z
|
|
551
579
|
.array(
|
|
552
580
|
z.union([z.number().int().positive(), z.string().regex(/^\d+\.\.\d+$/u)])
|
|
@@ -693,6 +721,7 @@ const searchConfigSchema = z
|
|
|
693
721
|
algolia: algoliaSearchSchema.optional(),
|
|
694
722
|
indexing: z
|
|
695
723
|
.strictObject({
|
|
724
|
+
includeCodeBlocks: z.boolean().default(false),
|
|
696
725
|
includeHiddenPages: z.boolean().default(false),
|
|
697
726
|
})
|
|
698
727
|
.prefault({}),
|
|
@@ -794,6 +823,20 @@ const askEndpointSchema = z
|
|
|
794
823
|
}
|
|
795
824
|
);
|
|
796
825
|
|
|
826
|
+
/** The object form of `ai.llmsTxt`; a bare boolean normalizes onto it. */
|
|
827
|
+
const llmsTxtObjectSchema = z.strictObject({
|
|
828
|
+
/**
|
|
829
|
+
* Markdown inserted after the title and summary, before the page sections:
|
|
830
|
+
* the llms.txt spec's "details" slot. The place to tell agents when to use
|
|
831
|
+
* the product and how to call it; trimmed, and dropped when blank.
|
|
832
|
+
*/
|
|
833
|
+
details: z.string().trim().min(1).optional(),
|
|
834
|
+
enabled: z.boolean().default(true),
|
|
835
|
+
openapi: z.boolean().default(true),
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
type LlmsTxtResolved = z.output<typeof llmsTxtObjectSchema>;
|
|
839
|
+
|
|
797
840
|
const aiConfigSchema = z.strictObject({
|
|
798
841
|
ask: z
|
|
799
842
|
.strictObject({
|
|
@@ -857,19 +900,16 @@ const aiConfigSchema = z.strictObject({
|
|
|
857
900
|
/**
|
|
858
901
|
* `llms.txt`/`llms-full.txt` emission. A bare boolean toggles it; the object
|
|
859
902
|
* form adds `openapi: false` to keep generated API reference pages out of
|
|
860
|
-
* both files (e.g. when the configured spec is example content)
|
|
903
|
+
* both files (e.g. when the configured spec is example content) and
|
|
904
|
+
* `details`, free-form Markdown placed after the summary — the llms.txt
|
|
905
|
+
* spec's details block, where a site tells agents when to reach for it.
|
|
861
906
|
*/
|
|
862
907
|
llmsTxt: z
|
|
863
|
-
.union([
|
|
864
|
-
z.boolean(),
|
|
865
|
-
z.strictObject({
|
|
866
|
-
enabled: z.boolean().default(true),
|
|
867
|
-
openapi: z.boolean().default(true),
|
|
868
|
-
}),
|
|
869
|
-
])
|
|
908
|
+
.union([z.boolean(), llmsTxtObjectSchema])
|
|
870
909
|
.default(true)
|
|
871
|
-
.transform(
|
|
872
|
-
|
|
910
|
+
.transform(
|
|
911
|
+
(value): LlmsTxtResolved =>
|
|
912
|
+
isBoolean(value) ? { enabled: value, openapi: true } : value
|
|
873
913
|
),
|
|
874
914
|
// Serializers for the agent-facing Markdown downlevel (the `.md` mirror,
|
|
875
915
|
// llms-full.txt, MCP get_page), keyed by JSX name. Functions live here —
|
|
@@ -954,11 +994,37 @@ const featuredLinkSchema = z.strictObject({
|
|
|
954
994
|
label: z.string(),
|
|
955
995
|
});
|
|
956
996
|
|
|
997
|
+
/**
|
|
998
|
+
* A header link: a plain one (`Log in`, `Status`), or the single call to
|
|
999
|
+
* action. Same shape as a featured link minus the icon — the header row is
|
|
1000
|
+
* text, not iconography.
|
|
1001
|
+
*/
|
|
1002
|
+
const headerActionSchema = z.strictObject({
|
|
1003
|
+
href: z.string(),
|
|
1004
|
+
label: z.string(),
|
|
1005
|
+
});
|
|
1006
|
+
|
|
957
1007
|
const navigationConfigSchema = z.strictObject({
|
|
1008
|
+
/** Plain links in the header, left of the icon buttons. */
|
|
1009
|
+
actions: z.array(headerActionSchema).default([]),
|
|
1010
|
+
/**
|
|
1011
|
+
* The one primary call to action in the header, as a filled button.
|
|
1012
|
+
*
|
|
1013
|
+
* Singular on purpose: a docs header has room for exactly one thing a
|
|
1014
|
+
* reader is being asked to do, a row of buttons asks for nothing, and
|
|
1015
|
+
* `featured` already takes the secondary links.
|
|
1016
|
+
*/
|
|
1017
|
+
cta: headerActionSchema.optional(),
|
|
958
1018
|
/** Pinned links shown above the generated sidebar sections. */
|
|
959
1019
|
featured: z.array(featuredLinkSchema).default([]),
|
|
960
|
-
/**
|
|
961
|
-
|
|
1020
|
+
/**
|
|
1021
|
+
* The GitHub link in the header. `true` derives it from `github`, `false`
|
|
1022
|
+
* hides it, and an absolute URL points it anywhere on GitHub — an
|
|
1023
|
+
* organization, say, when the docs repo itself is private and `github` has
|
|
1024
|
+
* to stay unset. The mark stays the GitHub one, so a URL elsewhere belongs in
|
|
1025
|
+
* `actions`.
|
|
1026
|
+
*/
|
|
1027
|
+
repo: z.union([z.boolean(), httpUrlSchema]).default(true),
|
|
962
1028
|
selectors: z.array(navSelectorSchema).default([]),
|
|
963
1029
|
/**
|
|
964
1030
|
* Sidebar behavior. `display` sets how every group renders (a group in an
|
|
@@ -1346,6 +1412,50 @@ const contentSignalsSchema = z
|
|
|
1346
1412
|
return value;
|
|
1347
1413
|
});
|
|
1348
1414
|
|
|
1415
|
+
/** A schema.org `PostalAddress`, any part of which may be given. */
|
|
1416
|
+
const postalAddressSchema = z.strictObject({
|
|
1417
|
+
addressCountry: z.string().optional(),
|
|
1418
|
+
addressLocality: z.string().optional(),
|
|
1419
|
+
addressRegion: z.string().optional(),
|
|
1420
|
+
postalCode: z.string().optional(),
|
|
1421
|
+
streetAddress: z.string().optional(),
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
/**
|
|
1425
|
+
* `seo.organization`: the organization behind the site, emitted on every page
|
|
1426
|
+
* as a schema.org `Organization` node (see `seo/jsonld.ts`). Name and URL
|
|
1427
|
+
* default to the site's; contact details become a `ContactPoint`, the address
|
|
1428
|
+
* a `PostalAddress` — what agents check to verify a business.
|
|
1429
|
+
*/
|
|
1430
|
+
const organizationConfigSchema = z.strictObject({
|
|
1431
|
+
address: postalAddressSchema.optional(),
|
|
1432
|
+
contactType: z.string().default("customer support"),
|
|
1433
|
+
email: z.email().optional(),
|
|
1434
|
+
logo: z.string().optional(),
|
|
1435
|
+
name: z.string().optional(),
|
|
1436
|
+
sameAs: z.array(z.url()).default([]),
|
|
1437
|
+
telephone: z.string().optional(),
|
|
1438
|
+
url: z.url().optional(),
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* `seo.software`: the product the site documents, emitted on the homepage as
|
|
1443
|
+
* a schema.org `SoftwareApplication` node. `true` takes every default (name
|
|
1444
|
+
* and description from the site, category `DeveloperApplication`).
|
|
1445
|
+
*/
|
|
1446
|
+
const softwareConfigSchema = z.strictObject({
|
|
1447
|
+
applicationCategory: z.string().default("DeveloperApplication"),
|
|
1448
|
+
description: z.string().optional(),
|
|
1449
|
+
license: z.string().optional(),
|
|
1450
|
+
name: z.string().optional(),
|
|
1451
|
+
operatingSystem: z.string().optional(),
|
|
1452
|
+
price: z.union([z.number().nonnegative(), z.string()]).optional(),
|
|
1453
|
+
priceCurrency: z.string().default("USD"),
|
|
1454
|
+
sameAs: z.array(z.url()).default([]),
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
type SoftwareResolved = z.output<typeof softwareConfigSchema>;
|
|
1458
|
+
|
|
1349
1459
|
/** Discoverability features: OG images, feeds, sitemap, structured data. */
|
|
1350
1460
|
const seoConfigSchema = z.strictObject({
|
|
1351
1461
|
/**
|
|
@@ -1357,21 +1467,59 @@ const seoConfigSchema = z.strictObject({
|
|
|
1357
1467
|
/** robots.txt `Content-Signal` usage declaration (on by default). */
|
|
1358
1468
|
contentSignals: contentSignalsSchema.prefault(true),
|
|
1359
1469
|
og: ogConfigSchema.default({}),
|
|
1470
|
+
/** The organization behind the site, as an `Organization` JSON-LD node. */
|
|
1471
|
+
organization: organizationConfigSchema.optional(),
|
|
1360
1472
|
/** Generate robots.txt (with a Sitemap reference when available). */
|
|
1361
1473
|
robots: z.boolean().default(true),
|
|
1362
1474
|
rss: rssConfigSchema.prefault({}),
|
|
1363
1475
|
/** Generate sitemap.xml (requires deployment.site). */
|
|
1364
1476
|
sitemap: z.boolean().default(true),
|
|
1477
|
+
/** The documented product, as a homepage `SoftwareApplication` node. */
|
|
1478
|
+
software: z
|
|
1479
|
+
.union([z.boolean(), softwareConfigSchema])
|
|
1480
|
+
.optional()
|
|
1481
|
+
.transform((value): SoftwareResolved | undefined => {
|
|
1482
|
+
if (value === true) {
|
|
1483
|
+
return softwareConfigSchema.parse({});
|
|
1484
|
+
}
|
|
1485
|
+
return value === false ? undefined : value;
|
|
1486
|
+
}),
|
|
1365
1487
|
/** Emit schema.org JSON-LD in each page's <head>. */
|
|
1366
1488
|
structuredData: z.boolean().default(true),
|
|
1367
1489
|
/** X (Twitter) account attribution for share cards. */
|
|
1368
1490
|
x: xConfigSchema.default({}),
|
|
1369
1491
|
});
|
|
1370
1492
|
|
|
1493
|
+
/**
|
|
1494
|
+
* The GitHub instance's origin, normalized. Repo, edit, and API URLs are all
|
|
1495
|
+
* built by appending to this, so it is reduced to a bare origin: a trailing
|
|
1496
|
+
* slash would double the separator, and a path, query, fragment, or embedded
|
|
1497
|
+
* credentials would land in the middle of every generated link.
|
|
1498
|
+
*/
|
|
1499
|
+
const githubOriginSchema = httpUrlSchema.transform(
|
|
1500
|
+
(value) => new URL(value).origin
|
|
1501
|
+
);
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* A REST API base: an origin plus an optional path, since Enterprise Server
|
|
1505
|
+
* serves the API from `/api/v3`. Anything past the path is dropped for the same
|
|
1506
|
+
* reason the host is reduced — `/repos/{owner}/{repo}` is appended as a string,
|
|
1507
|
+
* so a query would swallow the route and a fragment would strip it from the
|
|
1508
|
+
* request entirely, leaving a lookup that silently returns the wrong thing.
|
|
1509
|
+
*/
|
|
1510
|
+
const githubApiSchema = httpUrlSchema.transform((value) => {
|
|
1511
|
+
const { origin, pathname } = new URL(value);
|
|
1512
|
+
return trimEnd(`${origin}${pathname}`, "/");
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1371
1515
|
const githubConfigSchema = z.strictObject({
|
|
1516
|
+
/** REST API base. Derived from `host` when unset. */
|
|
1517
|
+
api: githubApiSchema.optional(),
|
|
1372
1518
|
branch: z.string().default("main"),
|
|
1373
1519
|
/** Path from the repo root to the project root (for monorepos). */
|
|
1374
1520
|
dir: z.string().optional(),
|
|
1521
|
+
/** Origin of the GitHub instance, for Enterprise installations. */
|
|
1522
|
+
host: githubOriginSchema.default(PUBLIC_HOST_URL),
|
|
1375
1523
|
owner: z.string(),
|
|
1376
1524
|
repo: z.string(),
|
|
1377
1525
|
});
|
|
@@ -1577,9 +1725,33 @@ export type OpenApiSource = z.input<typeof openapiSourceSchema>;
|
|
|
1577
1725
|
const scalarConfigSchema = z.record(z.string(), z.unknown()).optional();
|
|
1578
1726
|
|
|
1579
1727
|
/**
|
|
1580
|
-
* The
|
|
1728
|
+
* The interactive "Try it" panel on operation pages (Blume renderer). On by
|
|
1729
|
+
* default; `false` hides it. The object form keeps it on and sets `proxy`,
|
|
1730
|
+
* the CORS escape hatch the Send button routes requests through: a proxy URL,
|
|
1731
|
+
* or `true` for the built-in `/_api-proxy` endpoint (which requires
|
|
1732
|
+
* `deployment.output: "server"`). Booleans normalize to the object shape so
|
|
1733
|
+
* consumers read `{ enabled, proxy }` directly. `proxy` applies to the
|
|
1734
|
+
* HTTP-posting playgrounds (OpenAPI, GraphQL) — an event composer's WebSocket
|
|
1735
|
+
* connect is direct. One schema for every reference block, so the
|
|
1736
|
+
* normalization can never drift between them.
|
|
1737
|
+
*/
|
|
1738
|
+
const playgroundConfigSchema = z
|
|
1739
|
+
.union([
|
|
1740
|
+
z.boolean(),
|
|
1741
|
+
z.strictObject({
|
|
1742
|
+
enabled: z.boolean().default(true),
|
|
1743
|
+
proxy: z.union([z.boolean(), z.string()]).default(false),
|
|
1744
|
+
}),
|
|
1745
|
+
])
|
|
1746
|
+
.default(true)
|
|
1747
|
+
.transform((value) =>
|
|
1748
|
+
isBoolean(value) ? { enabled: value, proxy: false } : value
|
|
1749
|
+
);
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* The shared shape of the API-reference blocks — only the mount route and
|
|
1581
1753
|
* code-sample defaults differ per spec kind, so each block declares just
|
|
1582
|
-
* those.
|
|
1754
|
+
* those (the GraphQL block derives from this via omit/extend below).
|
|
1583
1755
|
*/
|
|
1584
1756
|
const referenceConfigSchema = (defaults: {
|
|
1585
1757
|
codeSamples: string[];
|
|
@@ -1591,27 +1763,8 @@ const referenceConfigSchema = (defaults: {
|
|
|
1591
1763
|
enabled: z.boolean().default(false),
|
|
1592
1764
|
/** Start nested schema rows expanded rather than collapsed (Blume renderer). */
|
|
1593
1765
|
expandSchemas: z.boolean().default(false),
|
|
1594
|
-
/**
|
|
1595
|
-
|
|
1596
|
-
* default; `false` hides it. The object form keeps it on and sets `proxy`,
|
|
1597
|
-
* the CORS escape hatch the OpenAPI Send button routes requests through: a
|
|
1598
|
-
* proxy URL, or `true` for the built-in `/_api-proxy` endpoint (which
|
|
1599
|
-
* requires `deployment.output: "server"`). Booleans normalize to the object
|
|
1600
|
-
* shape so consumers read `{ enabled, proxy }` directly. `proxy` is
|
|
1601
|
-
* OpenAPI-only — an event composer's WebSocket connect is direct.
|
|
1602
|
-
*/
|
|
1603
|
-
playground: z
|
|
1604
|
-
.union([
|
|
1605
|
-
z.boolean(),
|
|
1606
|
-
z.strictObject({
|
|
1607
|
-
enabled: z.boolean().default(true),
|
|
1608
|
-
proxy: z.union([z.boolean(), z.string()]).default(false),
|
|
1609
|
-
}),
|
|
1610
|
-
])
|
|
1611
|
-
.default(true)
|
|
1612
|
-
.transform((value) =>
|
|
1613
|
-
isBoolean(value) ? { enabled: value, proxy: false } : value
|
|
1614
|
-
),
|
|
1766
|
+
/** The "Try it" panel; see {@link playgroundConfigSchema}. */
|
|
1767
|
+
playground: playgroundConfigSchema,
|
|
1615
1768
|
/** Who renders the reference: Blume's own UI, or the embedded Scalar SPA. */
|
|
1616
1769
|
renderer: z.enum(["blume", "scalar"]).default("blume"),
|
|
1617
1770
|
/** Where the reference mounts. */
|
|
@@ -1651,6 +1804,43 @@ const asyncapiConfigSchema = referenceConfigSchema({
|
|
|
1651
1804
|
route: "/events",
|
|
1652
1805
|
});
|
|
1653
1806
|
|
|
1807
|
+
/**
|
|
1808
|
+
* A single GraphQL schema rendered by the reference. `spec` is a local path or
|
|
1809
|
+
* an `http(s)` URL to SDL text or an introspection JSON result; `endpoint` is
|
|
1810
|
+
* the live GraphQL API URL the playground and code samples target (a schema,
|
|
1811
|
+
* unlike an OpenAPI document, names no server).
|
|
1812
|
+
*/
|
|
1813
|
+
const graphqlSourceSchema = openapiSourceSchema.extend({
|
|
1814
|
+
/** URL of the live GraphQL endpoint (playground + code samples). */
|
|
1815
|
+
endpoint: z.string().optional(),
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
export type GraphqlSource = z.input<typeof graphqlSourceSchema>;
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* GraphQL reference. Blume lowers the schema (SDL or introspection JSON) to
|
|
1822
|
+
* one real page per root field — grouped as Queries/Mutations/Subscriptions —
|
|
1823
|
+
* plus one page per named type (Objects, Input Objects, Enums, Interfaces,
|
|
1824
|
+
* Unions, Scalars), all included in the sidebar, search, llms.txt, and OG.
|
|
1825
|
+
* Always Blume-rendered: the Scalar SPA reads OpenAPI documents only, so the
|
|
1826
|
+
* block declares no `renderer`/`scalar`/`theme` escape hatches.
|
|
1827
|
+
*/
|
|
1828
|
+
const graphqlConfigSchema = referenceConfigSchema({
|
|
1829
|
+
codeSamples: ["curl", "js", "python"],
|
|
1830
|
+
route: "/graphql",
|
|
1831
|
+
})
|
|
1832
|
+
// No `renderer`/`scalar`/`theme` escape hatches (the Scalar SPA reads
|
|
1833
|
+
// OpenAPI documents only) and no `expandSchemas` (GraphQL field tables have
|
|
1834
|
+
// no nesting) — everything else, the playground normalization included, is
|
|
1835
|
+
// the shared reference shape.
|
|
1836
|
+
.omit({ expandSchemas: true, renderer: true, scalar: true, theme: true })
|
|
1837
|
+
.extend({
|
|
1838
|
+
/** Default live endpoint URL for every source (per-source `endpoint` wins). */
|
|
1839
|
+
endpoint: z.string().optional(),
|
|
1840
|
+
/** One or more schemas; each renders on its own route by default. */
|
|
1841
|
+
sources: z.array(graphqlSourceSchema).default([]),
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1654
1844
|
/**
|
|
1655
1845
|
* Opt-in custom frontmatter keys. `extend` maps each extra key a project's
|
|
1656
1846
|
* pages may carry (e.g. `owner`, `reviewedAt`) to a validation schema; the
|
|
@@ -1736,6 +1926,7 @@ export const blumeConfigSchema = z
|
|
|
1736
1926
|
/** Opt-in custom frontmatter keys, validated by user-supplied schemas. */
|
|
1737
1927
|
frontmatter: frontmatterConfigSchema.prefault({}),
|
|
1738
1928
|
github: githubConfigSchema.optional(),
|
|
1929
|
+
graphql: graphqlConfigSchema.prefault({}),
|
|
1739
1930
|
i18n: i18nConfigSchema.optional(),
|
|
1740
1931
|
image: imageConfigSchema.prefault({}),
|
|
1741
1932
|
integrations: z.array(z.custom<AstroIntegration>()).default([]),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { needsPlaygroundProxy } from "../openapi/references.ts";
|
|
1
2
|
import { searchProviderMeta } from "../search/providers.ts";
|
|
2
3
|
import type { ResolvedConfig } from "./schema.ts";
|
|
3
4
|
|
|
@@ -14,15 +15,10 @@ export const serverFeatures = (config: ResolvedConfig): string[] => {
|
|
|
14
15
|
if (config.ai.mcp.enabled) {
|
|
15
16
|
features.push("MCP server");
|
|
16
17
|
}
|
|
17
|
-
// The built-in playground proxy (`
|
|
18
|
-
// live fetch endpoint at `/_api-proxy`; an external
|
|
19
|
-
// proxy-less playground stays fully static.
|
|
20
|
-
if (
|
|
21
|
-
config.openapi.enabled &&
|
|
22
|
-
config.openapi.renderer === "blume" &&
|
|
23
|
-
config.openapi.playground.enabled &&
|
|
24
|
-
config.openapi.playground.proxy === true
|
|
25
|
-
) {
|
|
18
|
+
// The built-in playground proxy (`playground.proxy: true` on the OpenAPI or
|
|
19
|
+
// GraphQL block) is a live fetch endpoint at `/_api-proxy`; an external
|
|
20
|
+
// proxy URL (string) or a proxy-less playground stays fully static.
|
|
21
|
+
if (needsPlaygroundProxy(config)) {
|
|
26
22
|
features.push("API playground proxy");
|
|
27
23
|
}
|
|
28
24
|
// Mixedbread (and any future provider) that proxies queries through a secret
|
|
@@ -5,6 +5,7 @@ import { gfm } from "micromark-extension-gfm";
|
|
|
5
5
|
import stringWidth from "string-width";
|
|
6
6
|
|
|
7
7
|
import matter from "../frontmatter.ts";
|
|
8
|
+
import { PUBLIC_API_URL } from "../github.ts";
|
|
8
9
|
import { columnsPrefix } from "../text-width.ts";
|
|
9
10
|
import {
|
|
10
11
|
hashText,
|
|
@@ -55,7 +56,6 @@ interface GithubRelease {
|
|
|
55
56
|
tag_name: string;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const DEFAULT_BASE_URL = "https://api.github.com";
|
|
59
59
|
const DEFAULT_LIMIT = 100;
|
|
60
60
|
const PER_PAGE = 100;
|
|
61
61
|
|
|
@@ -203,7 +203,7 @@ export const githubReleasesSource = (
|
|
|
203
203
|
ctx: SourceContext
|
|
204
204
|
): ContentSource => {
|
|
205
205
|
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
206
|
-
const base = (options.baseUrl ??
|
|
206
|
+
const base = (options.baseUrl ?? PUBLIC_API_URL).replace(/\/$/u, "");
|
|
207
207
|
const max = options.limit ?? DEFAULT_LIMIT;
|
|
208
208
|
const cache = snapshotCache(ctx.cacheDir);
|
|
209
209
|
let snapshot = new Map<string, SourceEntry>();
|