@jdevalk/astro-seo-graph 0.8.0 → 0.9.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/AGENTS.md +167 -0
- package/README.md +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +37 -0
- package/dist/integration.d.ts.map +1 -1
- package/dist/integration.js +37 -6
- package/dist/integration.js.map +1 -1
- package/dist/llms-txt.d.ts +51 -0
- package/dist/llms-txt.d.ts.map +1 -0
- package/dist/llms-txt.js +53 -0
- package/dist/llms-txt.js.map +1 -0
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -24,6 +24,35 @@ Two packages:
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
## Contents
|
|
28
|
+
|
|
29
|
+
**Schema core** — concepts and builders for the JSON-LD graph:
|
|
30
|
+
- [Architecture](#architecture)
|
|
31
|
+
- [Installation](#installation)
|
|
32
|
+
- [The @id system](#the-id-system)
|
|
33
|
+
- [Piece builders reference](#piece-builders-reference)
|
|
34
|
+
|
|
35
|
+
**Recipes and patterns** — how to model real sites:
|
|
36
|
+
- [Site type recipes](#site-type-recipes)
|
|
37
|
+
- [Trust and credibility signals](#trust-and-credibility-signals)
|
|
38
|
+
- [Choosing the right Article subtype](#choosing-the-right-article-subtype)
|
|
39
|
+
- [Actions: telling agents what they can do](#actions-telling-agents-what-they-can-do)
|
|
40
|
+
- [Multi-type entities](#multi-type-entities)
|
|
41
|
+
- [Rich Organization patterns](#rich-organization-patterns)
|
|
42
|
+
- [Rich Person patterns](#rich-person-patterns)
|
|
43
|
+
- [Reference implementations](#reference-implementations)
|
|
44
|
+
|
|
45
|
+
**Astro integration** — runtime component and build-time checks:
|
|
46
|
+
- [Astro integration guide](#astro-integration-guide) — the `<Seo>` component, hreflang, schema endpoints
|
|
47
|
+
- [Build-time integration](#build-time-integration) — `seoGraph()` hook, H1 validation, metadata uniqueness, IndexNow, `llms.txt`
|
|
48
|
+
- [Complete integration example](#complete-integration-example)
|
|
49
|
+
- [Advanced patterns](#advanced-patterns)
|
|
50
|
+
- [Common mistakes](#common-mistakes)
|
|
51
|
+
- [Validating your output](#validating-your-output)
|
|
52
|
+
- [Repository structure](#repository-structure)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
27
56
|
## Architecture
|
|
28
57
|
|
|
29
58
|
```
|
|
@@ -2576,6 +2605,144 @@ const links = buildAlternateLinks({
|
|
|
2576
2605
|
|
|
2577
2606
|
---
|
|
2578
2607
|
|
|
2608
|
+
## Build-time integration
|
|
2609
|
+
|
|
2610
|
+
`@jdevalk/astro-seo-graph/integration` exports a default `seoGraph()`
|
|
2611
|
+
function that returns an Astro integration. It hooks `astro:build:done`
|
|
2612
|
+
to run cross-page SEO checks and optional post-build actions against the
|
|
2613
|
+
static HTML output. SSR pages aren't on disk at build time, so they're
|
|
2614
|
+
not checked.
|
|
2615
|
+
|
|
2616
|
+
```js
|
|
2617
|
+
// astro.config.mjs
|
|
2618
|
+
import { defineConfig } from 'astro/config';
|
|
2619
|
+
import seoGraph from '@jdevalk/astro-seo-graph/integration';
|
|
2620
|
+
|
|
2621
|
+
export default defineConfig({
|
|
2622
|
+
site: 'https://example.com',
|
|
2623
|
+
integrations: [
|
|
2624
|
+
seoGraph({
|
|
2625
|
+
// All options below are optional; listed with defaults.
|
|
2626
|
+
validateH1: true,
|
|
2627
|
+
validateUniqueMetadata: true,
|
|
2628
|
+
// indexNow: { ... },
|
|
2629
|
+
// llmsTxt: { ... },
|
|
2630
|
+
}),
|
|
2631
|
+
],
|
|
2632
|
+
});
|
|
2633
|
+
```
|
|
2634
|
+
|
|
2635
|
+
### Options
|
|
2636
|
+
|
|
2637
|
+
| Option | Default | Purpose |
|
|
2638
|
+
| ------------------------ | ------- | -------------------------------------------------------------------------- |
|
|
2639
|
+
| `validateH1` | `true` | Warn when a page has zero or >1 `<h1>` elements. |
|
|
2640
|
+
| `validateUniqueMetadata` | `true` | Warn when two pages share the same `<title>` or meta description. |
|
|
2641
|
+
| `indexNow` | — | Submit built URLs to IndexNow. Omit to disable. |
|
|
2642
|
+
| `llmsTxt` | — | Generate `llms.txt` at the root of the build output. Omit to disable. |
|
|
2643
|
+
|
|
2644
|
+
### H1 and metadata validation
|
|
2645
|
+
|
|
2646
|
+
`validateH1` flags pages with missing or duplicate `<h1>` elements — the
|
|
2647
|
+
most common on-page SEO/accessibility miss. `validateUniqueMetadata`
|
|
2648
|
+
flags `<title>` or `<meta name="description">` values that repeat across
|
|
2649
|
+
pages; duplicates hurt Google's ability to pick a canonical result and
|
|
2650
|
+
can only be spotted across the whole corpus.
|
|
2651
|
+
|
|
2652
|
+
Both extractors are exported for reuse:
|
|
2653
|
+
|
|
2654
|
+
```ts
|
|
2655
|
+
import { countH1s, extractTitle, extractMetaDescription } from '@jdevalk/astro-seo-graph/integration';
|
|
2656
|
+
```
|
|
2657
|
+
|
|
2658
|
+
### IndexNow submission
|
|
2659
|
+
|
|
2660
|
+
```js
|
|
2661
|
+
seoGraph({
|
|
2662
|
+
indexNow: {
|
|
2663
|
+
key: process.env.INDEXNOW_KEY!, // 8–128 hex chars
|
|
2664
|
+
host: 'example.com',
|
|
2665
|
+
siteUrl: 'https://example.com',
|
|
2666
|
+
// keyLocation?: defaults to https://<host>/<key>.txt
|
|
2667
|
+
// endpoint?: defaults to api.indexnow.org
|
|
2668
|
+
// filter?: (url) => boolean — drop URLs before submission
|
|
2669
|
+
},
|
|
2670
|
+
});
|
|
2671
|
+
```
|
|
2672
|
+
|
|
2673
|
+
Only URLs on `host` are submitted. `index.html` paths are rewritten to
|
|
2674
|
+
their trailing-slash form.
|
|
2675
|
+
|
|
2676
|
+
**Deploy the key file first.** IndexNow verifies ownership by fetching
|
|
2677
|
+
`https://<host>/<key>.txt` on every submission. Submissions sent before
|
|
2678
|
+
the key is reachable in production get rejected (HTTP 403) and the key
|
|
2679
|
+
is treated as invalid — you'll have to rotate it. Serve the key via
|
|
2680
|
+
`createIndexNowKeyRoute` (see below), deploy, confirm the `.txt` loads
|
|
2681
|
+
over HTTPS, *then* enable `indexNow` in the integration.
|
|
2682
|
+
|
|
2683
|
+
```ts
|
|
2684
|
+
// src/pages/[your-key-here].txt.ts
|
|
2685
|
+
import { createIndexNowKeyRoute } from '@jdevalk/astro-seo-graph';
|
|
2686
|
+
|
|
2687
|
+
export const GET = createIndexNowKeyRoute({ key: 'your-key-here' });
|
|
2688
|
+
```
|
|
2689
|
+
|
|
2690
|
+
The filename (minus `.txt.ts`) must equal the key.
|
|
2691
|
+
|
|
2692
|
+
### llms.txt generation
|
|
2693
|
+
|
|
2694
|
+
Generates an [`llms.txt`](https://llmstxt.org) file — a markdown summary
|
|
2695
|
+
of the site that LLMs can use as a concise entry point.
|
|
2696
|
+
|
|
2697
|
+
```js
|
|
2698
|
+
seoGraph({
|
|
2699
|
+
llmsTxt: {
|
|
2700
|
+
title: 'Example Site',
|
|
2701
|
+
siteUrl: 'https://example.com',
|
|
2702
|
+
summary: 'A demo site about X, Y, and Z.',
|
|
2703
|
+
// details?: extra paragraphs between summary and sections
|
|
2704
|
+
// sections?: user-supplied sections (skips auto-collection)
|
|
2705
|
+
// filter?: (url) => boolean — drop URLs from auto-section
|
|
2706
|
+
// autoSectionName?: defaults to 'Pages'
|
|
2707
|
+
// outputPath?: defaults to 'llms.txt'
|
|
2708
|
+
},
|
|
2709
|
+
});
|
|
2710
|
+
```
|
|
2711
|
+
|
|
2712
|
+
By default, one "Pages" section is auto-generated from every built HTML
|
|
2713
|
+
file's `<title>` + meta description. Supply `sections` to take full
|
|
2714
|
+
control of the structure:
|
|
2715
|
+
|
|
2716
|
+
```js
|
|
2717
|
+
llmsTxt: {
|
|
2718
|
+
title: 'Example Site',
|
|
2719
|
+
siteUrl: 'https://example.com',
|
|
2720
|
+
sections: [
|
|
2721
|
+
{
|
|
2722
|
+
name: 'Docs',
|
|
2723
|
+
links: [
|
|
2724
|
+
{ url: 'https://example.com/docs/intro/', title: 'Intro', description: 'Start here' },
|
|
2725
|
+
],
|
|
2726
|
+
},
|
|
2727
|
+
{ name: 'Blog', links: [/* ... */] },
|
|
2728
|
+
],
|
|
2729
|
+
}
|
|
2730
|
+
```
|
|
2731
|
+
|
|
2732
|
+
The renderer is also exported for non-Astro contexts:
|
|
2733
|
+
|
|
2734
|
+
```ts
|
|
2735
|
+
import { renderLlmsTxt } from '@jdevalk/astro-seo-graph';
|
|
2736
|
+
|
|
2737
|
+
const markdown = renderLlmsTxt({
|
|
2738
|
+
title: 'Example',
|
|
2739
|
+
summary: 'A demo site.',
|
|
2740
|
+
sections: [{ name: 'Pages', links: [{ url: 'https://x/', title: 'Home' }] }],
|
|
2741
|
+
});
|
|
2742
|
+
```
|
|
2743
|
+
|
|
2744
|
+
---
|
|
2745
|
+
|
|
2579
2746
|
## Complete integration example
|
|
2580
2747
|
|
|
2581
2748
|
Here's how joost.blog wires everything together. Use this as a reference
|
package/README.md
CHANGED
|
@@ -343,6 +343,8 @@ actions. Currently:
|
|
|
343
343
|
built corpus.
|
|
344
344
|
- Optionally submits built URLs to [IndexNow](https://www.indexnow.org)
|
|
345
345
|
after the build completes.
|
|
346
|
+
- Optionally generates an [`llms.txt`](https://llmstxt.org) file at the
|
|
347
|
+
root of the build output, summarising the site for LLMs.
|
|
346
348
|
|
|
347
349
|
```js
|
|
348
350
|
// astro.config.mjs
|
|
@@ -369,12 +371,20 @@ Options:
|
|
|
369
371
|
| `validateH1` | `true` | Warn about pages without exactly one `<h1>` |
|
|
370
372
|
| `validateUniqueMetadata` | `true` | Warn about duplicate `<title>` or meta description across pages |
|
|
371
373
|
| `indexNow` | — | Submit built URLs to IndexNow. See below for sub-options. |
|
|
374
|
+
| `llmsTxt` | — | Generate `llms.txt` at the build root. See below for sub-options. |
|
|
372
375
|
|
|
373
376
|
`indexNow` sub-options: `key` (8–128 hex chars), `host` (bare host, e.g.
|
|
374
377
|
`example.com`), `siteUrl` (absolute origin), `keyLocation?` (defaults to
|
|
375
378
|
`https://<host>/<key>.txt`), `endpoint?` (defaults to `api.indexnow.org`),
|
|
376
379
|
`filter?` (drop URLs for which the callback returns `false`).
|
|
377
380
|
|
|
381
|
+
`llmsTxt` sub-options: `title` (required H1), `siteUrl` (required, used to
|
|
382
|
+
resolve crawled HTML paths), `summary?` (rendered as a blockquote),
|
|
383
|
+
`details?` (extra paragraphs), `sections?` (user-supplied sections; when
|
|
384
|
+
given, no pages are auto-collected), `filter?` (drop URLs from the
|
|
385
|
+
auto-generated section), `autoSectionName?` (defaults to `Pages`),
|
|
386
|
+
`outputPath?` (defaults to `llms.txt`).
|
|
387
|
+
|
|
378
388
|
`index.html` paths are rewritten to their trailing-slash form. Only static
|
|
379
389
|
pages are checked/submitted (SSR pages aren't on disk at build time).
|
|
380
390
|
|
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,6 @@ export { breadcrumbsFromUrl } from './breadcrumbs.js';
|
|
|
11
11
|
export type { BreadcrumbsFromUrlInput } from './breadcrumbs.js';
|
|
12
12
|
export { createIndexNowKeyRoute, submitToIndexNow, validateIndexNowKey } from './indexnow.js';
|
|
13
13
|
export type { IndexNowKeyRouteOptions, IndexNowSubmitResult } from './indexnow.js';
|
|
14
|
+
export { renderLlmsTxt } from './llms-txt.js';
|
|
15
|
+
export type { LlmsTxtInput, LlmsTxtSection, LlmsTxtLink } from './llms-txt.js';
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC9F,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,qBAAqB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3F,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC9F,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,5 @@ export { buildAstroSeoProps } from './components/seo-props.js';
|
|
|
10
10
|
export { buildAlternateLinks } from './alternates.js';
|
|
11
11
|
export { breadcrumbsFromUrl } from './breadcrumbs.js';
|
|
12
12
|
export { createIndexNowKeyRoute, submitToIndexNow, validateIndexNowKey } from './indexnow.js';
|
|
13
|
+
export { renderLlmsTxt } from './llms-txt.js';
|
|
13
14
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAG9F,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/integration.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LlmsTxtSection } from './llms-txt.js';
|
|
1
2
|
interface BuildDoneHook {
|
|
2
3
|
dir: URL;
|
|
3
4
|
logger: {
|
|
@@ -39,6 +40,37 @@ export interface IndexNowIntegrationOptions {
|
|
|
39
40
|
*/
|
|
40
41
|
filter?: (url: string) => boolean;
|
|
41
42
|
}
|
|
43
|
+
export interface LlmsTxtIntegrationOptions {
|
|
44
|
+
/** H1 of the generated `llms.txt`. */
|
|
45
|
+
title: string;
|
|
46
|
+
/** Absolute site origin used to resolve built HTML paths into URLs. */
|
|
47
|
+
siteUrl: string;
|
|
48
|
+
/** Optional blockquote summary shown under the title. */
|
|
49
|
+
summary?: string;
|
|
50
|
+
/** Optional paragraphs of context between summary and sections. */
|
|
51
|
+
details?: string;
|
|
52
|
+
/**
|
|
53
|
+
* User-supplied sections. When provided, these are used verbatim and
|
|
54
|
+
* no pages are auto-collected from the build output. When omitted, a
|
|
55
|
+
* single "Pages" section is generated from all built HTML files.
|
|
56
|
+
*/
|
|
57
|
+
sections?: LlmsTxtSection[];
|
|
58
|
+
/**
|
|
59
|
+
* Filter which crawled URLs end up in the auto-generated section.
|
|
60
|
+
* Ignored when `sections` is supplied.
|
|
61
|
+
*/
|
|
62
|
+
filter?: (url: string) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Section name used for auto-generated pages. Defaults to `Pages`.
|
|
65
|
+
* Ignored when `sections` is supplied.
|
|
66
|
+
*/
|
|
67
|
+
autoSectionName?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Output file path relative to the build output directory. Defaults
|
|
70
|
+
* to `llms.txt`.
|
|
71
|
+
*/
|
|
72
|
+
outputPath?: string;
|
|
73
|
+
}
|
|
42
74
|
export interface SeoGraphIntegrationOptions {
|
|
43
75
|
/**
|
|
44
76
|
* Warn when a built page has zero or more than one `<h1>` element.
|
|
@@ -62,6 +94,11 @@ export interface SeoGraphIntegrationOptions {
|
|
|
62
94
|
* `index.html` are rewritten to their directory form.
|
|
63
95
|
*/
|
|
64
96
|
indexNow?: IndexNowIntegrationOptions;
|
|
97
|
+
/**
|
|
98
|
+
* Generate an `llms.txt` file at the root of the build output. Omit
|
|
99
|
+
* to disable.
|
|
100
|
+
*/
|
|
101
|
+
llmsTxt?: LlmsTxtIntegrationOptions;
|
|
65
102
|
}
|
|
66
103
|
/**
|
|
67
104
|
* Turn a built HTML file path (relative to the outDir, e.g.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAKnE,UAAU,aAAa;IACnB,GAAG,EAAE,GAAG,CAAC;IAET,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;CAC5F;AAED,UAAU,oBAAoB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtE,CAAC;CACL;AAED,MAAM,WAAW,0BAA0B;IACvC,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACtC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IACvC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,0BAA0B,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAc3E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAgBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlE;AAgBD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,OAAO,GAAE,0BAA+B,GAAG,oBAAoB,CAqK/F"}
|
package/dist/integration.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { readFile, readdir } from 'node:fs/promises';
|
|
1
|
+
import { readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join, relative } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { submitToIndexNow } from '@jdevalk/seo-graph-core';
|
|
5
|
+
import { renderLlmsTxt } from './llms-txt.js';
|
|
5
6
|
/**
|
|
6
7
|
* Turn a built HTML file path (relative to the outDir, e.g.
|
|
7
8
|
* `blog/post/index.html`) into an absolute URL on `siteUrl`. Rewrites
|
|
@@ -105,14 +106,18 @@ async function collectHtmlFiles(dir, base = dir) {
|
|
|
105
106
|
* ```
|
|
106
107
|
*/
|
|
107
108
|
export default function seoGraph(options = {}) {
|
|
108
|
-
const { validateH1 = true, validateUniqueMetadata = true, indexNow } = options;
|
|
109
|
+
const { validateH1 = true, validateUniqueMetadata = true, indexNow, llmsTxt, } = options;
|
|
110
|
+
const autoLlmsTxt = llmsTxt && !llmsTxt.sections;
|
|
109
111
|
return {
|
|
110
112
|
name: '@jdevalk/astro-seo-graph',
|
|
111
113
|
hooks: {
|
|
112
114
|
'astro:build:done': async ({ dir, logger }) => {
|
|
113
115
|
const buildDir = fileURLToPath(dir);
|
|
114
|
-
const needsContentScan = validateH1 || validateUniqueMetadata;
|
|
115
|
-
const htmlFiles = needsContentScan || indexNow
|
|
116
|
+
const needsContentScan = validateH1 || validateUniqueMetadata || autoLlmsTxt;
|
|
117
|
+
const htmlFiles = needsContentScan || indexNow || llmsTxt
|
|
118
|
+
? await collectHtmlFiles(buildDir)
|
|
119
|
+
: [];
|
|
120
|
+
const autoLinks = [];
|
|
116
121
|
const h1Missing = [];
|
|
117
122
|
const h1Multiple = [];
|
|
118
123
|
const titlesByValue = new Map();
|
|
@@ -127,20 +132,32 @@ export default function seoGraph(options = {}) {
|
|
|
127
132
|
else if (count > 1)
|
|
128
133
|
h1Multiple.push({ file, count });
|
|
129
134
|
}
|
|
135
|
+
const title = validateUniqueMetadata || autoLlmsTxt ? extractTitle(content) : null;
|
|
136
|
+
const description = validateUniqueMetadata || autoLlmsTxt
|
|
137
|
+
? extractMetaDescription(content)
|
|
138
|
+
: null;
|
|
130
139
|
if (validateUniqueMetadata) {
|
|
131
|
-
const title = extractTitle(content);
|
|
132
140
|
if (title) {
|
|
133
141
|
const list = titlesByValue.get(title) ?? [];
|
|
134
142
|
list.push(file);
|
|
135
143
|
titlesByValue.set(title, list);
|
|
136
144
|
}
|
|
137
|
-
const description = extractMetaDescription(content);
|
|
138
145
|
if (description) {
|
|
139
146
|
const list = descriptionsByValue.get(description) ?? [];
|
|
140
147
|
list.push(file);
|
|
141
148
|
descriptionsByValue.set(description, list);
|
|
142
149
|
}
|
|
143
150
|
}
|
|
151
|
+
if (autoLlmsTxt && llmsTxt) {
|
|
152
|
+
const url = htmlFileToUrl(file, llmsTxt.siteUrl);
|
|
153
|
+
if (!llmsTxt.filter || llmsTxt.filter(url)) {
|
|
154
|
+
autoLinks.push({
|
|
155
|
+
url,
|
|
156
|
+
title: title ?? url,
|
|
157
|
+
description: description ?? undefined,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
144
161
|
}
|
|
145
162
|
}
|
|
146
163
|
if (validateH1) {
|
|
@@ -199,6 +216,20 @@ export default function seoGraph(options = {}) {
|
|
|
199
216
|
}
|
|
200
217
|
}
|
|
201
218
|
}
|
|
219
|
+
if (llmsTxt) {
|
|
220
|
+
const sections = llmsTxt.sections ??
|
|
221
|
+
[{ name: llmsTxt.autoSectionName ?? 'Pages', links: autoLinks }];
|
|
222
|
+
const rendered = renderLlmsTxt({
|
|
223
|
+
title: llmsTxt.title,
|
|
224
|
+
summary: llmsTxt.summary,
|
|
225
|
+
details: llmsTxt.details,
|
|
226
|
+
sections,
|
|
227
|
+
});
|
|
228
|
+
const outPath = join(buildDir, llmsTxt.outputPath ?? 'llms.txt');
|
|
229
|
+
await writeFile(outPath, rendered, 'utf8');
|
|
230
|
+
const linkCount = sections.reduce((n, s) => n + s.links.length, 0);
|
|
231
|
+
logger.info(`llms.txt: wrote ${relative(buildDir, outPath)} with ${linkCount} link(s).`);
|
|
232
|
+
}
|
|
202
233
|
},
|
|
203
234
|
},
|
|
204
235
|
};
|
package/dist/integration.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build an `llms.txt` file — a markdown document that gives LLMs a
|
|
3
|
+
* concise, structured map of a site. Spec: https://llmstxt.org
|
|
4
|
+
*
|
|
5
|
+
* Shape:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* # Title
|
|
9
|
+
*
|
|
10
|
+
* > Summary (optional blockquote)
|
|
11
|
+
*
|
|
12
|
+
* Details paragraph(s) (optional)
|
|
13
|
+
*
|
|
14
|
+
* ## Section name
|
|
15
|
+
*
|
|
16
|
+
* - [Link title](url): optional description
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Callers can supply sections explicitly or let the integration
|
|
20
|
+
* auto-generate a single "Pages" section from crawled HTML.
|
|
21
|
+
*/
|
|
22
|
+
export interface LlmsTxtLink {
|
|
23
|
+
/** Absolute URL. */
|
|
24
|
+
url: string;
|
|
25
|
+
/** Link label. Usually the page `<title>`. */
|
|
26
|
+
title: string;
|
|
27
|
+
/** Optional short description. Usually the meta description. */
|
|
28
|
+
description?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface LlmsTxtSection {
|
|
31
|
+
/** Section heading (rendered as `## name`). */
|
|
32
|
+
name: string;
|
|
33
|
+
links: LlmsTxtLink[];
|
|
34
|
+
}
|
|
35
|
+
export interface LlmsTxtInput {
|
|
36
|
+
/** H1 of the document. Required. */
|
|
37
|
+
title: string;
|
|
38
|
+
/** Short blockquote summary shown under the title. */
|
|
39
|
+
summary?: string;
|
|
40
|
+
/** Extra paragraphs of context between summary and sections. */
|
|
41
|
+
details?: string;
|
|
42
|
+
sections: LlmsTxtSection[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render an `LlmsTxtInput` to a markdown string. Pure.
|
|
46
|
+
*
|
|
47
|
+
* Empty sections are omitted. A trailing newline is included so the file
|
|
48
|
+
* ends cleanly on disk.
|
|
49
|
+
*/
|
|
50
|
+
export declare function renderLlmsTxt(input: LlmsTxtInput): string;
|
|
51
|
+
//# sourceMappingURL=llms-txt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms-txt.d.ts","sourceRoot":"","sources":["../src/llms-txt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,WAAW;IACxB,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC3B,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IACzB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC9B;AAMD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAwBzD"}
|
package/dist/llms-txt.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build an `llms.txt` file — a markdown document that gives LLMs a
|
|
3
|
+
* concise, structured map of a site. Spec: https://llmstxt.org
|
|
4
|
+
*
|
|
5
|
+
* Shape:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* # Title
|
|
9
|
+
*
|
|
10
|
+
* > Summary (optional blockquote)
|
|
11
|
+
*
|
|
12
|
+
* Details paragraph(s) (optional)
|
|
13
|
+
*
|
|
14
|
+
* ## Section name
|
|
15
|
+
*
|
|
16
|
+
* - [Link title](url): optional description
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* Callers can supply sections explicitly or let the integration
|
|
20
|
+
* auto-generate a single "Pages" section from crawled HTML.
|
|
21
|
+
*/
|
|
22
|
+
function escapeLinkTitle(title) {
|
|
23
|
+
return title.replace(/\[/g, '\\[').replace(/\]/g, '\\]');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Render an `LlmsTxtInput` to a markdown string. Pure.
|
|
27
|
+
*
|
|
28
|
+
* Empty sections are omitted. A trailing newline is included so the file
|
|
29
|
+
* ends cleanly on disk.
|
|
30
|
+
*/
|
|
31
|
+
export function renderLlmsTxt(input) {
|
|
32
|
+
const parts = [`# ${input.title.trim()}`];
|
|
33
|
+
if (input.summary?.trim()) {
|
|
34
|
+
parts.push(`> ${input.summary.trim()}`);
|
|
35
|
+
}
|
|
36
|
+
if (input.details?.trim()) {
|
|
37
|
+
parts.push(input.details.trim());
|
|
38
|
+
}
|
|
39
|
+
for (const section of input.sections) {
|
|
40
|
+
if (section.links.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
parts.push(`## ${section.name.trim()}`);
|
|
43
|
+
const lines = section.links.map((link) => {
|
|
44
|
+
const label = escapeLinkTitle(link.title.trim() || link.url);
|
|
45
|
+
const base = `- [${label}](${link.url})`;
|
|
46
|
+
const desc = link.description?.trim();
|
|
47
|
+
return desc ? `${base}: ${desc}` : base;
|
|
48
|
+
});
|
|
49
|
+
parts.push(lines.join('\n'));
|
|
50
|
+
}
|
|
51
|
+
return parts.join('\n\n') + '\n';
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=llms-txt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llms-txt.js","sourceRoot":"","sources":["../src/llms-txt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA2BH,SAAS,eAAe,CAAC,KAAa;IAClC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC7C,MAAM,KAAK,GAAa,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEpD,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACzC,KAAK,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC;YACzC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5C,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;AACrC,CAAC"}
|
package/package.json
CHANGED