indexnowkit/sitemap
A Laravel package for generating XML sitemaps and integrating with IndexNow to notify search engines of URL changes. It helps automate sitemap creation and keep search indexes up to date with minimal configuration.
Assumption: the package under review is the Laravel/framework adapter that wraps indexnowkit/core and exposes artisan sitemap; the release notes are for its 0.9.0, whose headline feature is --new-only plus Sitemap\SeenStoreInterface.
Architecture fit. The change is additive and well-shaped for Laravel: a small interface (unseen() / remember()) that the host application can bind in the service container, plus an appended, named runner parameter (SitemapServices::runner(…, seen:)) so the runner is still constructible the old way. Deferring the state store to the consumer is idiomatic for a framework adapter — Laravel has cache, database, and filesystem primitives to back it — but it means the headline capability of this release is not usable out of the box in Laravel. That is the single most important fact for planning: "framework adapters have none in this wave" is an explicit statement that the CLI ships more functionality than the Laravel package does.
Integration feasibility. Moderate. The interface is trivially implementable (two methods over an iterable of entries keyed by URL + ISO 8601 lastmod, or empty). The real work is not the code but the semantics: choosing a backing store, defining a retention policy for fingerprints, and deciding whether persistence lives in cache (fast, lossy, cache clears cause mass re-announcement) or the database (durable, needs a migration and pruning job). Both are half a day to a day of engineering plus review.
Technical risk.
lastmod is part of the key — so every content change produces a new record. Without a prune or an upsert-by-URL strategy, the store grows monotonically.core ^0.13.1; both are 0.x, so nothing here is contractually frozen, and seen: as a named argument is now part of the public surface.Definitions::sitemap() declares --new-only for every adapter, so Laravel users will see the flag in --help and get exit 2 with NO_SEEN_STORE unless the app supplies a store. Expect support tickets., N new or changed; any log parser or monitoring rule keyed on that line needs review.Key questions.
remember() per batch give us a natural place to persist progress mid-run?seen: intended as public API, or an internal seam that could be renamed pre-1.0?Stack fit. Good. No new infrastructure is required beyond a store the application almost certainly already has (cache or DB), and the runner's per-batch remember() call maps cleanly onto Laravel's chunked iteration and job-batch idioms if we later want to queue submissions.
Migration path. Low friction, no breaking changes expected: the runner parameter is appended and named, so existing positional construction and SitemapServices::runner(...) calls remain valid; existing sitemap invocations without --new-only keep the previous behaviour exactly (the store is consulted only when the option is present, and never under --dry-run). The only mandatory step is a composer update to satisfy core ^0.13.1, which should be checked against any pinned indexnowkit/core constraint in composer.lock and against the CLI if both are installed in the same project.
Compatibility. Verify the PHP and Laravel version matrix for the bumped core, and confirm that nothing in our codebase type-hints against SitemapRunner's constructor signature in a way that breaks on an added parameter. If we already wrap the runner, we should extend the wrapper now rather than later.
Sequencing.
core to ≥0.13.1 and the adapter to 0.9.0 in a branch; run the existing test suite and one --dry-run to confirm output-format changes.SeenStoreInterface behind a container binding, with an explicit config switch and a null/no-op default so behaviour stays opt-in.--new-only) submission to prime the store; verify the , N new or changed count matches expectations.--new-only on the frequent schedule, keeping a periodic full run as a safety net.Maintenance. The store is now ours to own: schema or cache-key layout, pruning of superseded fingerprints, and a documented reset procedure. Budget a recurring upkeep line, not just a one-off build. Watch upstream for the first-party Laravel store so we can retire our implementation rather than maintain it indefinitely.
Support. Documentation must state plainly that --new-only requires an application-provided store on Laravel, and that the exit-2 NO_SEEN_STORE message is expected rather than a bug. Without that, the most likely support ticket in the next quarter is "the new flag doesn't work."
Scaling. Reads are one unseen() pass over the filtered entry set per run; cost scales linearly with sitemap size and is dominated by the store's lookup pattern. Implement it as a set-membership check (hashed key, indexed column) rather than a row-by-row query. Persist writes per batch rather than at the end so a mid-run crash does not lose all progress.
Failure modes.
remember() not called for failed batches: entries are re-announced next run. This is at-least-once by design and the correct default; document it so nobody "fixes" it into at-most-once.--dry-run followed by --new-only: dry runs never persist, so the preview and the real run can disagree; call this out in runbooks.--changed-since + --new-only: window applies first, then the store — operators debugging "why wasn't this submitted" must reason about both filters in order.Ramp-up. Low for the option itself (one flag on an existing scheduled command); moderate for the store (a new persistent component with a lifecycle). Estimate one engineer-day to implement and test the store, a further half-day for config, pruning, and runbook, and a one-week observation window on the frequent schedule before trusting --new-only as the primary path.
How can I help you explore Laravel packages today?