@jdevalk/astro-seo-graph 1.0.1 → 1.1.1

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 CHANGED
@@ -2634,6 +2634,9 @@ export default defineConfig({
2634
2634
  // All options below are optional; listed with defaults.
2635
2635
  validateH1: true,
2636
2636
  validateUniqueMetadata: true,
2637
+ validateImageAlt: true,
2638
+ validateMetadataLength: true, // or { title: { max: 60 }, ... }
2639
+ validateInternalLinks: true, // or { skip: (href) => href.startsWith('/api/') }
2637
2640
  // indexNow: { ... },
2638
2641
  // llmsTxt: { ... },
2639
2642
  }),
@@ -2643,12 +2646,15 @@ export default defineConfig({
2643
2646
 
2644
2647
  ### Options
2645
2648
 
2646
- | Option | Default | Purpose |
2647
- | ------------------------ | ------- | --------------------------------------------------------------------- |
2648
- | `validateH1` | `true` | Warn when a page has zero or >1 `<h1>` elements. |
2649
- | `validateUniqueMetadata` | `true` | Warn when two pages share the same `<title>` or meta description. |
2650
- | `indexNow` | | Submit built URLs to IndexNow. Omit to disable. |
2651
- | `llmsTxt` | | Generate `llms.txt` at the root of the build output. Omit to disable. |
2649
+ | Option | Default | Purpose |
2650
+ | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
2651
+ | `validateH1` | `true` | Warn when a page has zero or >1 `<h1>` elements. |
2652
+ | `validateUniqueMetadata` | `true` | Warn when two pages share the same `<title>` or meta description. |
2653
+ | `validateImageAlt` | `true` | Warn when a page has `<img>` tags missing an `alt` attribute. |
2654
+ | `validateMetadataLength` | `true` | Warn when `<title>` or description length falls outside SERP-friendly bounds (title 30–65, description 70–200 by default; overridable per-field). |
2655
+ | `validateInternalLinks` | `true` | Warn on trailing-slash mismatches and links to pages not in the build. Accepts `{ skip }` to exclude SSR-only routes. |
2656
+ | `indexNow` | — | Submit built URLs to IndexNow. Omit to disable. |
2657
+ | `llmsTxt` | — | Generate `llms.txt` at the root of the build output. Omit to disable. |
2652
2658
 
2653
2659
  ### H1 and metadata validation
2654
2660
 
@@ -2656,15 +2662,57 @@ export default defineConfig({
2656
2662
  most common on-page SEO/accessibility miss. `validateUniqueMetadata`
2657
2663
  flags `<title>` or `<meta name="description">` values that repeat across
2658
2664
  pages; duplicates hurt Google's ability to pick a canonical result and
2659
- can only be spotted across the whole corpus.
2665
+ can only be spotted across the whole corpus. `validateImageAlt` flags
2666
+ `<img>` tags missing an `alt` attribute. WCAG-sanctioned decorative
2667
+ markers are respected: `alt=""` (the canonical pattern) and
2668
+ `role="presentation"`/`role="none"` (removes the image from the
2669
+ accessibility tree) are not flagged. Only a tag with neither triggers a
2670
+ warning.
2660
2671
 
2661
- Both extractors are exported for reuse:
2672
+ `validateMetadataLength` flags `<title>` and `<meta name="description">`
2673
+ values outside the configured bounds. Pass `true` for the defaults
2674
+ (title 30–65, description 70–200) or an object to override per-field:
2675
+
2676
+ ```ts
2677
+ validateMetadataLength: {
2678
+ title: { min: 40, max: 60 },
2679
+ description: { max: 160 },
2680
+ },
2681
+ ```
2682
+
2683
+ `validateInternalLinks` scans `<a href>` values across every built page
2684
+ and flags two classes of issue: trailing-slash mismatches (e.g. linking
2685
+ to `/about-me` when the built page is `/about-me/` — "works" via
2686
+ redirect but wastes a round-trip on every click) and true 404s (links
2687
+ to paths not in the build). Only same-origin (via `config.site`) and
2688
+ root-relative links are checked; external URLs, `mailto:`, `tel:`, and
2689
+ fragment-only links are skipped.
2690
+
2691
+ Explicit redirects are honored by default: sources in
2692
+ `public/_redirects` (Netlify / Cloudflare Pages format) and literal
2693
+ keys in Astro's `redirects` config are treated as valid link targets.
2694
+ Dynamic rules (`*`, `:splat`, `[slug]` params) are skipped — use
2695
+ `skip` for those cases. Set `honorRedirects: false` to opt out (useful
2696
+ when auditing for redirect hops).
2697
+
2698
+ Pass `{ skip: (href) => boolean }` to exclude SSR-only routes or paths
2699
+ handled at the host/CDN layer.
2700
+
2701
+ The extractors, helpers, and the length resolver are exported for reuse:
2662
2702
 
2663
2703
  ```ts
2664
2704
  import {
2705
+ classifyInternalLink,
2706
+ collectAstroRedirectSources,
2665
2707
  countH1s,
2708
+ DEFAULT_METADATA_LENGTH_BOUNDS,
2709
+ extractAnchorHrefs,
2666
2710
  extractTitle,
2667
2711
  extractMetaDescription,
2712
+ findImagesWithoutAlt,
2713
+ htmlFileToPath,
2714
+ parseNetlifyRedirects,
2715
+ resolveMetadataLengthBounds,
2668
2716
  } from '@jdevalk/astro-seo-graph/integration';
2669
2717
  ```
2670
2718
 
package/README.md CHANGED
@@ -346,6 +346,15 @@ actions. Currently:
346
346
  common SEO and accessibility issue).
347
347
  - Warns about duplicate `<title>` or meta description values across the
348
348
  built corpus.
349
+ - Warns about `<img>` tags missing an `alt` attribute. Decorative
350
+ images marked with `alt=""` or `role="presentation"`/`role="none"`
351
+ are respected and not flagged; only a fully missing attribute is.
352
+ - Warns about `<title>` and meta description lengths outside
353
+ SERP-friendly bounds (defaults: title 30–65, description 70–200).
354
+ Bounds are configurable.
355
+ - Warns about internal `<a href>` links that point to a URL missing a
356
+ trailing slash (or carrying an extra one) relative to the built
357
+ page. Also flags links to paths that aren't in the build (true 404s).
349
358
  - Optionally submits built URLs to [IndexNow](https://www.indexnow.org)
350
359
  after the build completes.
351
360
  - Optionally generates an [`llms.txt`](https://llmstxt.org) file at the
@@ -371,12 +380,15 @@ export default defineConfig({
371
380
 
372
381
  Options:
373
382
 
374
- | Prop | Default | Description |
375
- | ------------------------ | ------- | ----------------------------------------------------------------- |
376
- | `validateH1` | `true` | Warn about pages without exactly one `<h1>` |
377
- | `validateUniqueMetadata` | `true` | Warn about duplicate `<title>` or meta description across pages |
378
- | `indexNow` | | Submit built URLs to IndexNow. See below for sub-options. |
379
- | `llmsTxt` | | Generate `llms.txt` at the build root. See below for sub-options. |
383
+ | Prop | Default | Description |
384
+ | ------------------------ | ------- | ------------------------------------------------------------------------------ |
385
+ | `validateH1` | `true` | Warn about pages without exactly one `<h1>` |
386
+ | `validateUniqueMetadata` | `true` | Warn about duplicate `<title>` or meta description across pages |
387
+ | `validateImageAlt` | `true` | Warn about `<img>` tags missing an `alt` attribute |
388
+ | `validateMetadataLength` | `true` | Warn when title or description length is outside configured bounds (see below) |
389
+ | `validateInternalLinks` | `true` | Warn on trailing-slash mismatches and links to pages not in the build |
390
+ | `indexNow` | — | Submit built URLs to IndexNow. See below for sub-options. |
391
+ | `llmsTxt` | — | Generate `llms.txt` at the build root. See below for sub-options. |
380
392
 
381
393
  `indexNow` sub-options: `key` (8–128 hex chars), `host` (bare host, e.g.
382
394
  `example.com`), `siteUrl` (absolute origin), `keyLocation?` (defaults to
@@ -384,6 +396,44 @@ Options:
384
396
  `filter?` (drop URLs for which the callback returns `false`; composed on
385
397
  top of the built-in `/404` exclusion).
386
398
 
399
+ `validateMetadataLength` accepts `true`/`false` for the defaults, or an
400
+ object to override bounds. Length is measured on the whitespace-collapsed,
401
+ entity-decoded text — the same thing Google renders in the SERP.
402
+
403
+ ```js
404
+ validateMetadataLength: {
405
+ title: { min: 40, max: 60 }, // tighter than the default 30–65
406
+ description: { max: 160 }, // keep description min at its default (70)
407
+ },
408
+ ```
409
+
410
+ `validateInternalLinks` scans every built page's `<a href>` values and
411
+ checks them against the set of paths actually produced by the build.
412
+ Catches the common "I linked to `/about-me` but the page is `/about-me/`"
413
+ bug (which "works" via redirect but wastes a round-trip). Only checks
414
+ same-origin links (via `config.site`) and root-relative `/foo`-style
415
+ hrefs; `mailto:`, `tel:`, fragment-only (`#x`), and off-origin URLs are
416
+ skipped.
417
+
418
+ Explicit redirects are honored as valid targets by default. Sources in
419
+ `public/_redirects` (Netlify / Cloudflare Pages format) and literal
420
+ keys in Astro's `redirects` config are unioned into the built-paths
421
+ set, so linking to a redirect source doesn't warn. Dynamic rules
422
+ (wildcards, splats, `[slug]` params) are skipped — glob matching is
423
+ out of scope; use `skip` for those cases. Set `honorRedirects: false`
424
+ to opt out — useful when you're actively eliminating redirect hops and
425
+ want to see every internal link that doesn't hit a built page
426
+ directly.
427
+
428
+ Pass a `skip` callback to exclude specific hrefs — useful for SSR-only
429
+ routes or paths handled at the host/CDN layer:
430
+
431
+ ```js
432
+ validateInternalLinks: {
433
+ skip: (href) => href.startsWith('/api/') || href === '/search',
434
+ },
435
+ ```
436
+
387
437
  The `/404` page is always excluded — no one needs search engines notified
388
438
  about it, and submitting it wastes daily IndexNow quota. Use `filter` to
389
439
  exclude site-specific utility pages:
@@ -7,9 +7,25 @@ interface BuildDoneHook {
7
7
  [key: string]: any;
8
8
  };
9
9
  }
10
+ /**
11
+ * Subset of `AstroConfig` we read in `astro:config:setup`. Only the
12
+ * fields we actually use, so the integration stays decoupled from
13
+ * Astro's full type surface.
14
+ */
15
+ interface ConfigSetupHook {
16
+ config: {
17
+ site?: string | URL;
18
+ trailingSlash?: 'always' | 'never' | 'ignore';
19
+ redirects?: Record<string, string | {
20
+ status?: number;
21
+ destination: string;
22
+ } | undefined>;
23
+ };
24
+ }
10
25
  interface AstroIntegrationLike {
11
26
  name: string;
12
27
  hooks: {
28
+ 'astro:config:setup'?: (args: ConfigSetupHook) => void | Promise<void>;
13
29
  'astro:build:done'?: (args: BuildDoneHook) => void | Promise<void>;
14
30
  };
15
31
  }
@@ -88,6 +104,76 @@ export interface LlmsTxtIntegrationOptions {
88
104
  */
89
105
  outputPath?: string;
90
106
  }
107
+ /**
108
+ * Options for `validateInternalLinks`. Pass `true` to enable with
109
+ * defaults, `false` to disable, or an object to customize.
110
+ */
111
+ export interface ValidateInternalLinksOptions {
112
+ /**
113
+ * Skip a specific href from validation. Return `true` to exclude.
114
+ * Useful for SSR-only routes, dynamic redirects, or paths handled
115
+ * at the host/CDN layer that aren't present in the build output.
116
+ *
117
+ * Receives the raw `href` attribute value as it appeared in the
118
+ * HTML (not resolved/normalized), so callers can match on the
119
+ * source form.
120
+ */
121
+ skip?: (href: string) => boolean;
122
+ /**
123
+ * Treat explicit redirect sources as valid link targets. Defaults
124
+ * to `true` — unions literal `from` paths from `public/_redirects`
125
+ * (Netlify / Cloudflare Pages format) and literal keys from
126
+ * Astro's `redirects` config into the built-paths set, so linking
127
+ * to an intentional redirect doesn't warn.
128
+ *
129
+ * Set to `false` to treat only actual built pages as valid. Useful
130
+ * when you want to surface "this link hits a redirect, even if
131
+ * that's on purpose" — e.g. during an audit pass where you're
132
+ * actively eliminating redirect hops for performance.
133
+ */
134
+ honorRedirects?: boolean;
135
+ }
136
+ /**
137
+ * Configurable bounds for `validateMetadataLength`. Missing fields fall
138
+ * back to the defaults (title 30–65, description 70–200).
139
+ */
140
+ export interface MetadataLengthBounds {
141
+ title?: {
142
+ min?: number;
143
+ max?: number;
144
+ };
145
+ description?: {
146
+ min?: number;
147
+ max?: number;
148
+ };
149
+ }
150
+ /** Defaults applied when `validateMetadataLength: true`. */
151
+ export declare const DEFAULT_METADATA_LENGTH_BOUNDS: {
152
+ readonly title: {
153
+ readonly min: 30;
154
+ readonly max: 65;
155
+ };
156
+ readonly description: {
157
+ readonly min: 70;
158
+ readonly max: 200;
159
+ };
160
+ };
161
+ /**
162
+ * Resolve the `validateMetadataLength` option into concrete bounds, or
163
+ * `null` when the check is disabled. Exported so callers can reuse the
164
+ * same resolution in their own pipelines (and so tests can cover the
165
+ * merge logic without running a full build).
166
+ */
167
+ export declare function resolveMetadataLengthBounds(option: boolean | MetadataLengthBounds | undefined): {
168
+ title: {
169
+ min: number;
170
+ max: number;
171
+ };
172
+ description: {
173
+ min: number;
174
+ max: number;
175
+ };
176
+ } | null;
91
177
  export interface SeoGraphIntegrationOptions {
92
178
  /**
93
179
  * Warn when a built page has zero or more than one `<h1>` element.
@@ -105,6 +191,44 @@ export interface SeoGraphIntegrationOptions {
105
191
  * are present on multiple pages.
106
192
  */
107
193
  validateUniqueMetadata?: boolean;
194
+ /**
195
+ * Warn when a built page contains `<img>` tags without an `alt`
196
+ * attribute. Missing alt text is both an accessibility and SEO
197
+ * issue. Defaults to `true`.
198
+ *
199
+ * Decorative images with `alt=""` are treated as correct — that's
200
+ * the intended WCAG pattern for images that convey no information.
201
+ * Only a fully missing `alt` attribute triggers a warning.
202
+ */
203
+ validateImageAlt?: boolean;
204
+ /**
205
+ * Warn when `<title>` or `<meta name="description">` length falls
206
+ * outside SERP-friendly bounds. Defaults to `true` (title 30–65
207
+ * chars, description 70–200 chars).
208
+ *
209
+ * Pass `false` to disable, or an object to override the bounds.
210
+ * Length is measured on the whitespace-collapsed, entity-decoded
211
+ * text — the same thing Google displays in the SERP.
212
+ */
213
+ validateMetadataLength?: boolean | MetadataLengthBounds;
214
+ /**
215
+ * Warn when an internal `<a href>` points to a URL that doesn't
216
+ * match a built page. Catches two common classes of bug:
217
+ *
218
+ * - **Trailing-slash mismatches.** Linking to `/about-me` when the
219
+ * site emits `/about-me/` (or vice versa). The link "works" via
220
+ * a redirect but wastes a round-trip and hurts SEO.
221
+ * - **Broken internal links.** A link to a path that isn't in the
222
+ * build at all — a 404 waiting to happen.
223
+ *
224
+ * Defaults to `true`. Only checks links with the same origin as
225
+ * `config.site` (plus root-relative `/foo`-style links). External
226
+ * links, `mailto:`, `tel:`, fragment-only (`#x`), and SSR-only
227
+ * routes are not checked. Pass `false` to disable, or
228
+ * `{ skip: (href) => boolean }` to exclude specific hrefs (e.g.
229
+ * dynamic redirects handled at the host layer).
230
+ */
231
+ validateInternalLinks?: boolean | ValidateInternalLinksOptions;
108
232
  /**
109
233
  * Submit built URLs to IndexNow after the build completes. Omit to
110
234
  * disable. Only URLs on `host` are submitted; URLs with trailing
@@ -130,6 +254,93 @@ export declare function htmlFileToUrl(relativePath: string, siteUrl: string): st
130
254
  * a lint warning.
131
255
  */
132
256
  export declare function countH1s(html: string): number;
257
+ /**
258
+ * Parse a Netlify/Cloudflare Pages `_redirects` file and return the
259
+ * `from` paths of rules with literal sources (no wildcards or
260
+ * placeholders). The literal subset is enough to treat those paths as
261
+ * valid link targets — if a rule uses `*` or `:splat` it's dynamic and
262
+ * we can't meaningfully pre-validate hrefs against it.
263
+ *
264
+ * Format per line: `from to [status]`. `#` starts a comment. Blank
265
+ * lines are ignored. See:
266
+ * https://docs.netlify.com/routing/redirects/
267
+ * https://developers.cloudflare.com/pages/configuration/redirects/
268
+ */
269
+ export declare function parseNetlifyRedirects(content: string): string[];
270
+ /**
271
+ * Return the literal `from` paths from an Astro `config.redirects`
272
+ * object. Parameterized routes (`/old/[slug]` or `/old/*`) are skipped
273
+ * for the same reason `parseNetlifyRedirects` skips dynamic rules.
274
+ */
275
+ export declare function collectAstroRedirectSources(redirects: ConfigSetupHook['config']['redirects'] | undefined): string[];
276
+ /**
277
+ * Turn a built HTML file path into its canonical pathname (the `url`
278
+ * form without origin). Used by `validateInternalLinks` to build the
279
+ * set of paths that actually exist in the build.
280
+ */
281
+ export declare function htmlFileToPath(relativePath: string): string;
282
+ /**
283
+ * Build the set of root-relative paths that count as valid link
284
+ * targets, given the full list of files produced by the build.
285
+ *
286
+ * HTML files become their pretty-URL form (via `htmlFileToPath`, so
287
+ * `blog/post/index.html` → `/blog/post/`). Every other file —
288
+ * static assets copied from `public/`, sitemap files, etc. — is
289
+ * included verbatim as `/<path>`, because those are valid link
290
+ * targets too even though they're not pages.
291
+ *
292
+ * Adding non-HTML files here is what stops `validateInternalLinks`
293
+ * from flagging `<a href="/images/foo.avif">` as a 404.
294
+ */
295
+ export declare function buildLinkTargetSet(files: readonly string[]): Set<string>;
296
+ /**
297
+ * Extract all `<a href="...">` values from an HTML string. Returns the
298
+ * raw attribute values (not decoded) in document order.
299
+ */
300
+ export declare function extractAnchorHrefs(html: string): string[];
301
+ /**
302
+ * Classify an internal link's href against the set of built pathnames.
303
+ *
304
+ * Returns:
305
+ * - `{ status: 'external' }` for off-origin, `mailto:`, `tel:`,
306
+ * fragment-only, or non-http(s) schemes.
307
+ * - `{ status: 'ok' }` when the resolved pathname exactly matches a
308
+ * built page.
309
+ * - `{ status: 'trailing-slash', suggested }` when the other form
310
+ * (with/without trailing slash) matches — your case.
311
+ * - `{ status: 'not-found' }` when no form matches — a broken link.
312
+ *
313
+ * `origin` is the site's canonical origin (from Astro's `config.site`).
314
+ * When omitted, only root-relative hrefs (`/foo`) are classified;
315
+ * absolute URLs return `external`.
316
+ */
317
+ export declare function classifyInternalLink(href: string, builtPaths: ReadonlySet<string>, origin: string | undefined): {
318
+ status: 'external';
319
+ } | {
320
+ status: 'ok';
321
+ } | {
322
+ status: 'trailing-slash';
323
+ suggested: string;
324
+ } | {
325
+ status: 'not-found';
326
+ };
327
+ /**
328
+ * Find `<img>` tags missing an `alt` attribute. Returns the `src` value
329
+ * of each offending tag (or `"(no src)"` when src is absent too) so the
330
+ * warning can identify which image to fix.
331
+ *
332
+ * Two WCAG-sanctioned ways to mark an image as decorative are respected
333
+ * and NOT flagged:
334
+ *
335
+ * - `alt=""` — the canonical decorative-image pattern.
336
+ * - `role="presentation"` (or `role="none"`) — removes the image from
337
+ * the accessibility tree; typically paired with `alt=""` but some
338
+ * codebases use it alone.
339
+ *
340
+ * Only a tag that has neither an `alt` attribute nor a decorative role
341
+ * triggers a warning.
342
+ */
343
+ export declare function findImagesWithoutAlt(html: string): string[];
133
344
  /**
134
345
  * Extract the first `<title>` element's text content. Returns `null` when
135
346
  * no title tag is found. Whitespace-collapsed and entity-decoded so
@@ -139,6 +350,11 @@ export declare function extractTitle(html: string): string | null;
139
350
  /**
140
351
  * Extract the `content` attribute of the first `<meta name="description">`
141
352
  * tag. Returns `null` when absent. Entity-decoded for duplicate detection.
353
+ *
354
+ * Uses a backreference on the opening quote so the capture terminates
355
+ * only on the *matching* quote — `content="don't stop"` keeps its
356
+ * apostrophe, and `content='She said "hi"'` keeps its double quotes.
357
+ * A naive `[^"']*` character class would cut both short.
142
358
  */
143
359
  export declare function extractMetaDescription(html: string): string | null;
144
360
  /**
@@ -1 +1 @@
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;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACrC;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQlE;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,CA6J/F"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,32 @@ export function isDefaultExcludedFromIndexNow(url) {
23
23
  }
24
24
  return pathname === '/404' || pathname === '/404/';
25
25
  }
26
+ /** Defaults applied when `validateMetadataLength: true`. */
27
+ export const DEFAULT_METADATA_LENGTH_BOUNDS = {
28
+ title: { min: 30, max: 65 },
29
+ description: { min: 70, max: 200 },
30
+ };
31
+ /**
32
+ * Resolve the `validateMetadataLength` option into concrete bounds, or
33
+ * `null` when the check is disabled. Exported so callers can reuse the
34
+ * same resolution in their own pipelines (and so tests can cover the
35
+ * merge logic without running a full build).
36
+ */
37
+ export function resolveMetadataLengthBounds(option) {
38
+ if (option === false || option === undefined)
39
+ return null;
40
+ const overrides = option === true ? {} : option;
41
+ return {
42
+ title: {
43
+ min: overrides.title?.min ?? DEFAULT_METADATA_LENGTH_BOUNDS.title.min,
44
+ max: overrides.title?.max ?? DEFAULT_METADATA_LENGTH_BOUNDS.title.max,
45
+ },
46
+ description: {
47
+ min: overrides.description?.min ?? DEFAULT_METADATA_LENGTH_BOUNDS.description.min,
48
+ max: overrides.description?.max ?? DEFAULT_METADATA_LENGTH_BOUNDS.description.max,
49
+ },
50
+ };
51
+ }
26
52
  /**
27
53
  * Turn a built HTML file path (relative to the outDir, e.g.
28
54
  * `blog/post/index.html`) into an absolute URL on `siteUrl`. Rewrites
@@ -56,6 +82,203 @@ export function countH1s(html) {
56
82
  const matches = html.match(/<h1[\s>]/gi);
57
83
  return matches ? matches.length : 0;
58
84
  }
85
+ /**
86
+ * Parse a Netlify/Cloudflare Pages `_redirects` file and return the
87
+ * `from` paths of rules with literal sources (no wildcards or
88
+ * placeholders). The literal subset is enough to treat those paths as
89
+ * valid link targets — if a rule uses `*` or `:splat` it's dynamic and
90
+ * we can't meaningfully pre-validate hrefs against it.
91
+ *
92
+ * Format per line: `from to [status]`. `#` starts a comment. Blank
93
+ * lines are ignored. See:
94
+ * https://docs.netlify.com/routing/redirects/
95
+ * https://developers.cloudflare.com/pages/configuration/redirects/
96
+ */
97
+ export function parseNetlifyRedirects(content) {
98
+ const sources = [];
99
+ for (const rawLine of content.split(/\r?\n/)) {
100
+ const line = rawLine.trim();
101
+ if (line === '' || line.startsWith('#'))
102
+ continue;
103
+ const from = line.split(/\s+/)[0];
104
+ if (!from)
105
+ continue;
106
+ // Skip dynamic rules — we only union literal paths into the
107
+ // built-paths set. Glob/splat matching is out of scope.
108
+ if (/[*:]/.test(from))
109
+ continue;
110
+ // Ignore non-path sources (full URLs in _redirects denote
111
+ // proxy/rewrite rules; not a link target we'd validate).
112
+ if (!from.startsWith('/'))
113
+ continue;
114
+ sources.push(from);
115
+ }
116
+ return sources;
117
+ }
118
+ /**
119
+ * Return the literal `from` paths from an Astro `config.redirects`
120
+ * object. Parameterized routes (`/old/[slug]` or `/old/*`) are skipped
121
+ * for the same reason `parseNetlifyRedirects` skips dynamic rules.
122
+ */
123
+ export function collectAstroRedirectSources(redirects) {
124
+ if (!redirects)
125
+ return [];
126
+ const sources = [];
127
+ for (const from of Object.keys(redirects)) {
128
+ if (!from.startsWith('/'))
129
+ continue;
130
+ if (/[[\]*]/.test(from))
131
+ continue;
132
+ sources.push(from);
133
+ }
134
+ return sources;
135
+ }
136
+ /**
137
+ * Turn a built HTML file path into its canonical pathname (the `url`
138
+ * form without origin). Used by `validateInternalLinks` to build the
139
+ * set of paths that actually exist in the build.
140
+ */
141
+ export function htmlFileToPath(relativePath) {
142
+ const normalized = relativePath.split(/[\\/]/).join('/');
143
+ if (normalized === 'index.html' || normalized === '/index.html')
144
+ return '/';
145
+ if (normalized.endsWith('/index.html')) {
146
+ return '/' + normalized.slice(0, -'index.html'.length);
147
+ }
148
+ if (normalized.endsWith('.html')) {
149
+ return '/' + normalized.slice(0, -'.html'.length);
150
+ }
151
+ return '/' + normalized;
152
+ }
153
+ /**
154
+ * Build the set of root-relative paths that count as valid link
155
+ * targets, given the full list of files produced by the build.
156
+ *
157
+ * HTML files become their pretty-URL form (via `htmlFileToPath`, so
158
+ * `blog/post/index.html` → `/blog/post/`). Every other file —
159
+ * static assets copied from `public/`, sitemap files, etc. — is
160
+ * included verbatim as `/<path>`, because those are valid link
161
+ * targets too even though they're not pages.
162
+ *
163
+ * Adding non-HTML files here is what stops `validateInternalLinks`
164
+ * from flagging `<a href="/images/foo.avif">` as a 404.
165
+ */
166
+ export function buildLinkTargetSet(files) {
167
+ const paths = new Set();
168
+ for (const file of files) {
169
+ if (file.endsWith('.html')) {
170
+ paths.add(htmlFileToPath(file));
171
+ }
172
+ else {
173
+ const normalized = file.split(/[\\/]/).join('/');
174
+ paths.add('/' + normalized);
175
+ }
176
+ }
177
+ return paths;
178
+ }
179
+ /**
180
+ * Extract all `<a href="...">` values from an HTML string. Returns the
181
+ * raw attribute values (not decoded) in document order.
182
+ */
183
+ export function extractAnchorHrefs(html) {
184
+ const hrefs = [];
185
+ for (const match of html.matchAll(/<a\b[^>]*\bhref\s*=\s*["']([^"']*)["'][^>]*>/gi)) {
186
+ hrefs.push(match[1]);
187
+ }
188
+ return hrefs;
189
+ }
190
+ /**
191
+ * Classify an internal link's href against the set of built pathnames.
192
+ *
193
+ * Returns:
194
+ * - `{ status: 'external' }` for off-origin, `mailto:`, `tel:`,
195
+ * fragment-only, or non-http(s) schemes.
196
+ * - `{ status: 'ok' }` when the resolved pathname exactly matches a
197
+ * built page.
198
+ * - `{ status: 'trailing-slash', suggested }` when the other form
199
+ * (with/without trailing slash) matches — your case.
200
+ * - `{ status: 'not-found' }` when no form matches — a broken link.
201
+ *
202
+ * `origin` is the site's canonical origin (from Astro's `config.site`).
203
+ * When omitted, only root-relative hrefs (`/foo`) are classified;
204
+ * absolute URLs return `external`.
205
+ */
206
+ export function classifyInternalLink(href, builtPaths, origin) {
207
+ if (!href)
208
+ return { status: 'external' };
209
+ // Same-page anchors and non-http schemes.
210
+ if (href.startsWith('#'))
211
+ return { status: 'external' };
212
+ if (/^(?:mailto:|tel:|javascript:|data:|blob:)/i.test(href))
213
+ return { status: 'external' };
214
+ let pathname;
215
+ if (href.startsWith('/') && !href.startsWith('//')) {
216
+ // Root-relative. Strip query + fragment.
217
+ pathname = href.split('#')[0].split('?')[0];
218
+ }
219
+ else if (/^https?:\/\//i.test(href) || href.startsWith('//')) {
220
+ // Absolute (or protocol-relative). Compare origins if we know ours.
221
+ if (!origin)
222
+ return { status: 'external' };
223
+ let parsed;
224
+ try {
225
+ parsed = new URL(href.startsWith('//') ? `https:${href}` : href);
226
+ }
227
+ catch {
228
+ return { status: 'external' };
229
+ }
230
+ let ourOrigin;
231
+ try {
232
+ ourOrigin = new URL(origin);
233
+ }
234
+ catch {
235
+ return { status: 'external' };
236
+ }
237
+ if (parsed.origin !== ourOrigin.origin)
238
+ return { status: 'external' };
239
+ pathname = parsed.pathname;
240
+ }
241
+ else {
242
+ // Relative or unknown shape — not worth guessing.
243
+ return { status: 'external' };
244
+ }
245
+ if (builtPaths.has(pathname))
246
+ return { status: 'ok' };
247
+ const flipped = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname + '/';
248
+ if (builtPaths.has(flipped)) {
249
+ return { status: 'trailing-slash', suggested: flipped };
250
+ }
251
+ return { status: 'not-found' };
252
+ }
253
+ /**
254
+ * Find `<img>` tags missing an `alt` attribute. Returns the `src` value
255
+ * of each offending tag (or `"(no src)"` when src is absent too) so the
256
+ * warning can identify which image to fix.
257
+ *
258
+ * Two WCAG-sanctioned ways to mark an image as decorative are respected
259
+ * and NOT flagged:
260
+ *
261
+ * - `alt=""` — the canonical decorative-image pattern.
262
+ * - `role="presentation"` (or `role="none"`) — removes the image from
263
+ * the accessibility tree; typically paired with `alt=""` but some
264
+ * codebases use it alone.
265
+ *
266
+ * Only a tag that has neither an `alt` attribute nor a decorative role
267
+ * triggers a warning.
268
+ */
269
+ export function findImagesWithoutAlt(html) {
270
+ const results = [];
271
+ for (const match of html.matchAll(/<img\b[^>]*>/gi)) {
272
+ const tag = match[0];
273
+ if (/\balt\s*=/i.test(tag))
274
+ continue;
275
+ if (/\brole\s*=\s*["'](?:presentation|none)["']/i.test(tag))
276
+ continue;
277
+ const src = tag.match(/\bsrc\s*=\s*["']([^"']*)["']/i)?.[1] ?? '(no src)';
278
+ results.push(src);
279
+ }
280
+ return results;
281
+ }
59
282
  const HTML_ENTITIES = {
60
283
  '&amp;': '&',
61
284
  '&lt;': '<',
@@ -83,26 +306,33 @@ export function extractTitle(html) {
83
306
  /**
84
307
  * Extract the `content` attribute of the first `<meta name="description">`
85
308
  * tag. Returns `null` when absent. Entity-decoded for duplicate detection.
309
+ *
310
+ * Uses a backreference on the opening quote so the capture terminates
311
+ * only on the *matching* quote — `content="don't stop"` keeps its
312
+ * apostrophe, and `content='She said "hi"'` keeps its double quotes.
313
+ * A naive `[^"']*` character class would cut both short.
86
314
  */
87
315
  export function extractMetaDescription(html) {
88
- const re = /<meta\s+[^>]*name\s*=\s*["']description["'][^>]*content\s*=\s*["']([^"']*)["'][^>]*>|<meta\s+[^>]*content\s*=\s*["']([^"']*)["'][^>]*name\s*=\s*["']description["'][^>]*>/i;
89
- const match = html.match(re);
316
+ // Two orderings: name-before-content or content-before-name.
317
+ const nameFirst = /<meta\b[^>]*?\bname\s*=\s*["']description["'][^>]*?\bcontent\s*=\s*(["'])([\s\S]*?)\1/i;
318
+ const contentFirst = /<meta\b[^>]*?\bcontent\s*=\s*(["'])([\s\S]*?)\1[^>]*?\bname\s*=\s*["']description["']/i;
319
+ const match = nameFirst.exec(html) ?? contentFirst.exec(html);
90
320
  if (!match)
91
321
  return null;
92
- const raw = (match[1] ?? match[2] ?? '').trim();
322
+ const raw = (match[2] ?? '').trim();
93
323
  if (!raw)
94
324
  return null;
95
325
  return decodeHtmlEntities(raw);
96
326
  }
97
- async function collectHtmlFiles(dir, base = dir) {
327
+ async function collectBuildFiles(dir, base = dir) {
98
328
  const entries = await readdir(dir, { withFileTypes: true });
99
329
  const files = [];
100
330
  for (const entry of entries) {
101
331
  const fullPath = join(dir, entry.name);
102
332
  if (entry.isDirectory()) {
103
- files.push(...(await collectHtmlFiles(fullPath, base)));
333
+ files.push(...(await collectBuildFiles(fullPath, base)));
104
334
  }
105
- else if (entry.isFile() && entry.name.endsWith('.html')) {
335
+ else if (entry.isFile()) {
106
336
  files.push(relative(base, fullPath));
107
337
  }
108
338
  }
@@ -126,20 +356,80 @@ async function collectHtmlFiles(dir, base = dir) {
126
356
  * ```
127
357
  */
128
358
  export default function seoGraph(options = {}) {
129
- const { validateH1 = true, validateUniqueMetadata = true, indexNow, llmsTxt } = options;
359
+ const { validateH1 = true, validateUniqueMetadata = true, validateImageAlt = true, validateMetadataLength = true, validateInternalLinks = true, indexNow, llmsTxt, } = options;
130
360
  const autoLlmsTxt = llmsTxt && !llmsTxt.sections;
361
+ const lengthBounds = resolveMetadataLengthBounds(validateMetadataLength);
362
+ const internalLinksEnabled = validateInternalLinks !== false;
363
+ const internalLinksSkip = typeof validateInternalLinks === 'object' ? validateInternalLinks.skip : undefined;
364
+ const internalLinksHonorRedirects = typeof validateInternalLinks === 'object'
365
+ ? (validateInternalLinks.honorRedirects ?? true)
366
+ : true;
367
+ // Captured in astro:config:setup so astro:build:done can classify
368
+ // absolute same-origin links against the site's canonical origin
369
+ // and treat Astro-configured redirect sources as valid link targets.
370
+ let siteOrigin;
371
+ let astroRedirectSources = [];
131
372
  return {
132
373
  name: '@jdevalk/astro-seo-graph',
133
374
  hooks: {
375
+ 'astro:config:setup': ({ config }) => {
376
+ if (config.site) {
377
+ try {
378
+ siteOrigin = new URL(config.site.toString()).origin;
379
+ }
380
+ catch {
381
+ // Leave undefined — validateInternalLinks will
382
+ // still work for root-relative hrefs.
383
+ }
384
+ }
385
+ astroRedirectSources = collectAstroRedirectSources(config.redirects);
386
+ },
134
387
  'astro:build:done': async ({ dir, logger }) => {
135
388
  const buildDir = fileURLToPath(dir);
136
- const needsContentScan = validateH1 || validateUniqueMetadata || autoLlmsTxt;
137
- const htmlFiles = needsContentScan || indexNow || llmsTxt ? await collectHtmlFiles(buildDir) : [];
389
+ const needsContentScan = validateH1 ||
390
+ validateUniqueMetadata ||
391
+ validateImageAlt ||
392
+ lengthBounds !== null ||
393
+ internalLinksEnabled ||
394
+ autoLlmsTxt;
395
+ const needsAnyFiles = needsContentScan || indexNow || llmsTxt || internalLinksEnabled;
396
+ const allFiles = needsAnyFiles ? await collectBuildFiles(buildDir) : [];
397
+ const htmlFiles = allFiles.filter((f) => f.endsWith('.html'));
398
+ let builtPaths = null;
399
+ if (internalLinksEnabled) {
400
+ // Include every build artefact, not just HTML pages.
401
+ // Static assets copied from public/ (images, fonts,
402
+ // downloads, etc.) are valid link targets too —
403
+ // filtering to .html would flag <a href="/images/foo.avif">
404
+ // as a 404.
405
+ builtPaths = buildLinkTargetSet(allFiles);
406
+ if (internalLinksHonorRedirects) {
407
+ // Treat explicit redirect sources as valid link
408
+ // targets. Reads `_redirects` (Netlify/Cloudflare
409
+ // Pages) from the build output and merges Astro's
410
+ // `config.redirects` captured at setup time.
411
+ try {
412
+ const redirectsContent = await readFile(join(buildDir, '_redirects'), 'utf8');
413
+ for (const from of parseNetlifyRedirects(redirectsContent)) {
414
+ builtPaths.add(from);
415
+ }
416
+ }
417
+ catch {
418
+ // No `_redirects` file; that's fine.
419
+ }
420
+ for (const from of astroRedirectSources) {
421
+ builtPaths.add(from);
422
+ }
423
+ }
424
+ }
138
425
  const autoLinks = [];
139
426
  const h1Missing = [];
140
427
  const h1Multiple = [];
141
428
  const titlesByValue = new Map();
142
429
  const descriptionsByValue = new Map();
430
+ const imagesMissingAlt = [];
431
+ const internalLinkIssues = [];
432
+ const lengthIssues = [];
143
433
  if (needsContentScan) {
144
434
  for (const file of htmlFiles) {
145
435
  const content = await readFile(join(buildDir, file), 'utf8');
@@ -150,8 +440,37 @@ export default function seoGraph(options = {}) {
150
440
  else if (count > 1)
151
441
  h1Multiple.push({ file, count });
152
442
  }
153
- const title = validateUniqueMetadata || autoLlmsTxt ? extractTitle(content) : null;
154
- const description = validateUniqueMetadata || autoLlmsTxt
443
+ if (validateImageAlt) {
444
+ const srcs = findImagesWithoutAlt(content);
445
+ if (srcs.length > 0)
446
+ imagesMissingAlt.push({ file, srcs });
447
+ }
448
+ if (builtPaths !== null) {
449
+ for (const href of extractAnchorHrefs(content)) {
450
+ if (internalLinksSkip && internalLinksSkip(href))
451
+ continue;
452
+ const result = classifyInternalLink(href, builtPaths, siteOrigin);
453
+ if (result.status === 'trailing-slash') {
454
+ internalLinkIssues.push({
455
+ file,
456
+ href,
457
+ kind: 'trailing-slash',
458
+ suggested: result.suggested,
459
+ });
460
+ }
461
+ else if (result.status === 'not-found') {
462
+ internalLinkIssues.push({
463
+ file,
464
+ href,
465
+ kind: 'not-found',
466
+ });
467
+ }
468
+ }
469
+ }
470
+ const needsTitle = validateUniqueMetadata || autoLlmsTxt || lengthBounds !== null;
471
+ const needsDescription = needsTitle;
472
+ const title = needsTitle ? extractTitle(content) : null;
473
+ const description = needsDescription
155
474
  ? extractMetaDescription(content)
156
475
  : null;
157
476
  if (validateUniqueMetadata) {
@@ -166,6 +485,52 @@ export default function seoGraph(options = {}) {
166
485
  descriptionsByValue.set(description, list);
167
486
  }
168
487
  }
488
+ if (lengthBounds !== null) {
489
+ if (title) {
490
+ if (title.length < lengthBounds.title.min) {
491
+ lengthIssues.push({
492
+ file,
493
+ field: 'title',
494
+ length: title.length,
495
+ bound: 'short',
496
+ limit: lengthBounds.title.min,
497
+ value: title,
498
+ });
499
+ }
500
+ else if (title.length > lengthBounds.title.max) {
501
+ lengthIssues.push({
502
+ file,
503
+ field: 'title',
504
+ length: title.length,
505
+ bound: 'long',
506
+ limit: lengthBounds.title.max,
507
+ value: title,
508
+ });
509
+ }
510
+ }
511
+ if (description) {
512
+ if (description.length < lengthBounds.description.min) {
513
+ lengthIssues.push({
514
+ file,
515
+ field: 'description',
516
+ length: description.length,
517
+ bound: 'short',
518
+ limit: lengthBounds.description.min,
519
+ value: description,
520
+ });
521
+ }
522
+ else if (description.length > lengthBounds.description.max) {
523
+ lengthIssues.push({
524
+ file,
525
+ field: 'description',
526
+ length: description.length,
527
+ bound: 'long',
528
+ limit: lengthBounds.description.max,
529
+ value: description,
530
+ });
531
+ }
532
+ }
533
+ }
169
534
  if (autoLlmsTxt && llmsTxt) {
170
535
  const url = htmlFileToUrl(file, llmsTxt.siteUrl);
171
536
  if (!llmsTxt.filter || llmsTxt.filter(url)) {
@@ -191,6 +556,47 @@ export default function seoGraph(options = {}) {
191
556
  }
192
557
  }
193
558
  }
559
+ if (builtPaths !== null) {
560
+ if (internalLinkIssues.length === 0) {
561
+ logger.info(`Internal links: ${htmlFiles.length} pages checked, all good.`);
562
+ }
563
+ else {
564
+ for (const issue of internalLinkIssues) {
565
+ if (issue.kind === 'trailing-slash') {
566
+ logger.warn(`Internal links: ${issue.file} links to ${JSON.stringify(issue.href)} but built page is ${JSON.stringify(issue.suggested)} (redirect on every visit).`);
567
+ }
568
+ else {
569
+ logger.warn(`Internal links: ${issue.file} links to ${JSON.stringify(issue.href)} which isn't in the build (404).`);
570
+ }
571
+ }
572
+ }
573
+ }
574
+ if (validateImageAlt) {
575
+ if (imagesMissingAlt.length === 0) {
576
+ logger.info(`Image alt validation: ${htmlFiles.length} pages checked, all good.`);
577
+ }
578
+ else {
579
+ for (const { file, srcs } of imagesMissingAlt) {
580
+ const preview = srcs.slice(0, 5).join(', ');
581
+ const more = srcs.length > 5 ? ` (+${srcs.length - 5} more)` : '';
582
+ logger.warn(`Image alt validation: ${file} has ${srcs.length} <img> without alt: ${preview}${more}`);
583
+ }
584
+ }
585
+ }
586
+ if (lengthBounds !== null) {
587
+ if (lengthIssues.length === 0) {
588
+ logger.info(`Metadata length: ${htmlFiles.length} pages checked, all good.`);
589
+ }
590
+ else {
591
+ for (const issue of lengthIssues) {
592
+ const direction = issue.bound === 'short' ? 'min' : 'max';
593
+ const preview = issue.value.length > 80
594
+ ? issue.value.slice(0, 77) + '…'
595
+ : issue.value;
596
+ logger.warn(`Metadata length: ${issue.file} ${issue.field} is ${issue.length} chars (${direction} ${issue.limit}): ${JSON.stringify(preview)}`);
597
+ }
598
+ }
599
+ }
194
600
  if (validateUniqueMetadata) {
195
601
  const dupTitles = [...titlesByValue.entries()].filter(([, files]) => files.length > 1);
196
602
  const dupDescriptions = [...descriptionsByValue.entries()].filter(([, files]) => files.length > 1);
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdevalk/astro-seo-graph",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "Astro integration for @jdevalk/seo-graph-core. Seo component, route factories, content-collection aggregator, Zod content helpers.",
5
5
  "keywords": [
6
6
  "astro",