Releases: jdevalk/emdash-plugin-seo
v0.11.0
Changed
peerDependencies.emdashbumped to^0.6.0. Picks up eager taxonomy-term hydration ongetEmDashEntryresults (emdash PR #626), plus the toolbar portableText fix (#685) and several cold-start / D1 replica performance improvements.
Added
- Taxonomy keywords and section in Article schema. For content pages the plugin now calls
getEmDashEntryto read the entry's eagerly-hydrateddata.termsmap (zero extra DB queries when the Astro template already fetched the entry in the same request). All assigned taxonomy terms are joined into thekeywordsfield of theBlogPostingnode. Terms from any taxonomy whose name starts withcategorare additionally mapped toarticleSection.
v0.10.0 — content-enumeration features now work
Requires emdash@^0.5.0
peerDependencies.emdash is now pinned to ^0.5.0. The three upstream PRs this plugin's content-enumeration features were waiting on all ship in that release:
- emdash-cms/emdash#536 —
slug/status/publishedAtonContentItem - emdash-cms/emdash#539 —
localeonContentItem - emdash-cms/emdash#540 —
where: { status?, locale? }onContentListOptions
Upgrade EmDash to ^0.5.0 before upgrading this plugin, or the peer check will complain.
Fixed
llms.txt(v0.6.0) now returns a real body with every published entry, grouped by collection. Previously returned an empty body on every real site because the iteration was reading stripped fields.- Schema map (v0.7.0) now returns the full list of published URLs. Previously returned
{items: []}for the same reason. - Fuzzy Redirects (v0.8.0) candidate list is now populated, so the suggester actually suggests. The other half (capturing original 404 URLs rather than the rewritten
/404) is still tracked in emdash-cms/emdash#525.
Removed
- Transient
@ts-expect-erroron the NLWebrelcontribution and per-call type casts inllms.ts/schema/endpoints.ts. - The "known broken" README warning.
Full Changelog: v0.9.0...v0.10.0
v0.9.0 — NLWeb link tag
Added
- NLWeb
<link>tag. When the NLWeb endpoint URL setting (new Agent discovery admin section) is populated, every rendered page carries<link rel=\"nlweb\" href=\"…\">advertising the site's conversational endpoint for agent discovery.
Requirements
Needs the EmDash release that contains emdash-cms/emdash#523 (merged upstream). On older EmDash versions the sandbox allowlist rejects the rel value and the contribution is silently dropped.
The contribution site carries a transient @ts-expect-error against the currently-published emdash types — it auto-unblocks once the new emdash version hits npm.
Status of other experimental features
Still blocked on upstream — see README warning table. No change since v0.8.0 for llms.txt, schema/map, or Fuzzy Redirects. Tracked at #530 (ContentItem missing slug/status/locale) and #525 (404 middleware captures wrong URL).
Full Changelog: v0.8.0...v0.9.0
v0.8.0 — Fuzzy Redirects
Added
-
Fuzzy Redirects admin tool. New admin page at SEO → Fuzzy Redirects that turns the core 404 log into a prioritized redirect work queue. For every logged 404, the tool fetches the current list of published URLs (via the plugin's own
schema/maproute), ranks live URLs against the missing path by path similarity, and lets you one-click create a 301 redirect for the chosen target.Scoring combines three signals:
- Levenshtein distance on the full normalized path (catches typos in inbound links).
- Token overlap (Jaccard index) on path segments (catches reordering and punctuation drift).
- Tokenized last-segment match bonus (catches slugs that survived a prefix rewrite like
/blog/old/hello→/posts/hello).
A minimum-score slider tunes aggressiveness. Created redirects are grouped under
seo-fuzzy-suggesterfor later audit. -
Exported
rankCandidates()andscoreSlugMatch()fromsrc/fuzzy.ts— reusable for the upcoming automatic-on-404 flow once thenotfoundhook lands upstream.
Companion upstream work
- emdash-cms/emdash#525 — proposed
notfoundhook that will let this tool run automatically on every 404 instead of requiring manual admin review. Same matching logic, automatic trigger.
Full Changelog: v0.7.0...v0.8.0
v0.7.0 — Schema map (experimental)
Added
- Schema map (experimental). New public plugin route
schema/mapreturning the list of every published URL backed by schema markup ({ items: [{ url, collection, updatedAt }] }). Wire it to/schemamap.xmlat your site root with the Astro snippet in the README so agents and crawlers can enumerate structured-data URLs without scraping HTML.
Per-URL schema endpoints (/schema/<slug>.json) are deferred pending an upstream helper for building page contexts from plugin routes — see emdash-cms/emdash#527.
Companion upstream work
- emdash-cms/emdash#523 — adds
nlwebto thepage:metadatalink-rel allowlist (unblocks a future NLWeb<link>tag contribution from this plugin). - emdash-cms/emdash#524 — proposed
page:robotshook (will let this plugin emit aSchemamap:directive inrobots.txt). - emdash-cms/emdash#525 — proposed
notfoundhook (foundation for a FuzzyRedirect feature). - emdash-cms/emdash#526 — proposed
content:validatehook (foundation for H1 / duplicate-title / schema-shape validation at save time).
Full Changelog: v0.6.0...v0.7.0
v0.6.0 — llms.txt (experimental)
Added
- llms.txt (experimental). Generates a small-form
llms.txtindex of all published content across every collection with aurlPattern, grouped by collection label. Exposed on the plugin routellms/txt; serve it from Astro by proxying that route at/llms.txt. Enabled by default — flip the llms.txt (experimental) toggle to disable. Only the plainllms.txtis implemented; thellms-full.txtvariant is out of scope. buildLlmsTxt()exported for consumers who want to assemble the body fromgetEmDashCollection()results with custom sectioning or ordering.
v0.5.0 — IndexNow
What's new
- IndexNow submission — on publish/unpublish transitions, the canonical URL of the affected content item is submitted to IndexNow so Bing, Yandex, Seznam, Naver, and Yep recrawl immediately. Opt-in via a single toggle in the SEO settings UI; a 32-character hex key is generated and persisted in plugin KV on first use.
Setup
Wire the IndexNow key-verification file into your Astro site using createIndexNowKeyRoute from @jdevalk/astro-seo-graph — see the README for details.
Dependency updates
- Requires
@jdevalk/seo-graph-core^0.6.0 - Requires
@jdevalk/astro-seo-graph^0.7.0
v0.4.2
Dependency-range bump for transitive dedup.
Changes
- Bump
@jdevalk/seo-graph-corerange from^0.2.0to^0.3.0 - Bump
@jdevalk/astro-seo-graphrange from^0.2.1to^0.2.4
Why
@jdevalk/astro-seo-graph@0.2.4 pins @jdevalk/seo-graph-core@0.3.0 as a direct dep. The plugin's previous ^0.2.0 range excluded 0.3.0 (pre-1.0 caret semver: ^0.2.0 means >=0.2.0 <0.3.0), so consumers ended up with two parallel copies of seo-graph-core in their node_modules — one for astro-seo-graph, one for this plugin. Widening the range to ^0.3.0 lets both resolutions converge on a single installed copy.
No behavioural change
seo-graph-core@0.3.0 ships three additive improvements (no inLanguage default on piece builders, optional WebPageInput.breadcrumb, generic type parameter on buildOrganization) that this plugin doesn't exercise directly. The plugin's rendered JSON-LD output is unchanged.
See CHANGELOG.md for details.
🤖 Generated with Claude Code
v0.4.1
Dependency-bump patch release.
Changes
- Bump
@jdevalk/seo-graph-corerange from^0.1.0to^0.2.0to match what@jdevalk/astro-seo-graph@^0.2.1pulls in transitively. Avoids duplicateseo-graph-corecopies in consumernode_modules.
Context
@jdevalk/seo-graph-core@0.2.0 shipped two additive API improvements:
buildOrganizationgained a generic type parameter (<T extends Organization = Organization>) withOrganizationInput.extratyped asPartial<T>— flows schema-dts subtype autocomplete into the escape-hatch field.WebPageInput.breadcrumbbecame optional.
This plugin doesn't call those piece builders directly, so there's no functional change. The bump is install-footprint hygiene.
See CHANGELOG.md for details.
🤖 Generated with Claude Code
v0.4.0
See CHANGELOG.md for full release notes.
Highlights
hreflang alternates for multilingual EmDash sites. When Astro i18n is configured and content entries are linked via translation_group, the plugin now emits one <link rel="alternate" hreflang="…" href="…"> per published sibling plus an automatically-resolved x-default. URLs are built from each collection's urlPattern + locale prefix rules, matching canonical URLs. BCP 47 tags are normalized on output (fr-ca → fr-CA).
Zero cost on single-locale sites. Gated on isI18nEnabled() before any database call.
New dependency
@jdevalk/astro-seo-graph@^0.2.1— runtime dep for the purebuildAlternateLinkshelper. Pure TypeScript, no Astro runtime impact (Astro is a peer dep satisfied transitively through EmDash).
Dependency range updates
@jdevalk/seo-graph-core:^0.1.0-alpha.0→^0.1.0
Tests
30 new tests: 12 URL-construction tests covering locale-prefix rules and BCP 47 region tags, 18 hreflang adapter tests including a reciprocity invariant that verifies the same URL set is emitted regardless of which sibling is the "current" page.
🤖 Generated with Claude Code