Skip to content

feat: add getSyndicationUrl hook to syndicator interface - #882

Draft
aciccarello wants to merge 4 commits into
getindiekit:mainfrom
aciccarello:feat/syndication-preprocess-hook-endpoints
Draft

feat: add getSyndicationUrl hook to syndicator interface#882
aciccarello wants to merge 4 commits into
getindiekit:mainfrom
aciccarello:feat/syndication-preprocess-hook-endpoints

Conversation

@aciccarello

Copy link
Copy Markdown
Collaborator

Closes #860

Summary

Adds an optional getSyndicationUrl(publication) hook to the syndicator plugin interface, enabling syndication URLs to be written into post files at creation time — without requiring a database or async syndication step.

This is needed for webmention-based syndication targets where the syndication URL is known at post creation time, but mp-syndicate-to is stripped before the file is written (correct per the Micropub spec — it is a server command, not a content property).

Changes

endpoint-syndicate

  • syndicate() is now optional on syndicator targets. Targets without it are skipped rather than throwing. This allows syndicators that only implement getSyndicationUrl() to register without providing an empty no-op.

endpoint-micropub

  • In post-content.js create(), after resolving syndication targets and before getPostTemplateProperties() strips mp-* keys, calls getSyndicationUrl(publication) on any target whose info.uid is in mp-syndicate-to.
  • Returned URLs are normalized to an array, deduplicated against existing syndication values (exact string match), and appended to properties.syndication.
  • Errors from getSyndicationUrl() are caught and logged via debug() — the create request is never failed.

Interface

// Optional method to add to a syndicator target
async getSyndicationUrl(publication) {
  // return string | string[] | null | undefined
}

A syndicator implementing this hook can return the syndication URL synchronously at post creation time. The endpoint handles appending it to syndication before the file is written.

This is scoped to create() only. Update behaviour is a separate concern.

@aciccarello
aciccarello marked this pull request as ready for review August 17, 2026 03:48
@aciccarello
aciccarello marked this pull request as draft August 17, 2026 18:10
@paulrobertlloyd paulrobertlloyd changed the title feat: add getSyndicationUrl hook to syndicator interface feat: add getSyndicationUrl hook to syndicator interface Aug 23, 2026
@paulrobertlloyd
paulrobertlloyd force-pushed the main branch 2 times, most recently from 67b3847 to fa1d368 Compare August 27, 2026 20:07
@rmdes

rmdes commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

The hook solves the ordering problem in the right place — running it in create() before the template renders is what makes the URL available to the file, which is the part that can't be fixed afterwards.

One thing worth surfacing, because this PR works around it without naming it. getSyndicationTarget matches by URL origin:

const targetOrigin = new URL(target.info.uid).origin;
const syndicateToOrigin = new URL(syndicateTo).origin;
return targetOrigin === syndicateToOrigin;

IndieNews registers one target per language on a single origin, so those collapse into one. With en and fr targets registered:

mp-syndicate-to  https://news.indieweb.org/fr   ->  IndieNews (en)

This PR matches info.uid exactly instead, which gets it right — but the codebase now has two matching rules for the same concept. Worth deciding which is intended, since the origin-based one is wrong for any syndicator registering several targets on one host. My own IndieNews syndicator has the same shape, so it hits this today.

Smaller point: getSyndicationUrl failures are caught and sent to debug(). For a hook returning a constant that's fine, but as a general interface a failure means the URL silently isn't written, with nothing to indicate why.

rmdes added a commit to rmdes/indiekit-syndicator-indienews that referenced this pull request Aug 27, 2026
Indiekit selects a syndication target by comparing URL origins, and every
IndieNews language shares one. A request for `https://news.indieweb.org/fr`
is therefore handed to whichever IndieNews target registered first, which is
usually the English one.

That is not only a wrong label. The webmention names the channel it is
submitted to, and IndieNews requires the post to link to that same channel,
so a post carrying a `u-syndication` link to `/fr` submitted with a target of
`/en` is rejected.

Read the language back out of `mp-syndicate-to`, which Indiekit passes to
`syndicate()`, and fall back to the configured one when no IndieNews target
was requested.

This covers one language per post. Requesting two in a single post still
loses the second: `hasSyndicationUrl` also compares origins, so once one
IndieNews permalink is recorded the rest are treated as already syndicated.
That needs the exact matching proposed upstream in getindiekit/indiekit#882.

Adds the first tests for this package, run with `node --test`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make syndication available to file at creation for webmention based syndication

2 participants