Imported from goobits/sitemap (
AGENTS.md). Install upstream withnpx skills add goobits/sitemap. Copyright stays with the author.
@goobits/sitemap Agent Guide
Reusable sitemap core, server helpers, and operations layer for SvelteKit (and any modern Fetch-API runtime). Notes here describe code that agents/contributors should follow when modifying this package.
Quick reference
- Category: library (ESM-only, TypeScript)
- Distribution: git submodule consumed inside a pnpm workspace. Consumer bundlers (Vite/esbuild/SvelteKit) compile the
.tssource directly — no build step, nodist/, no npm publish. - Primary stack: TypeScript 6 + vitest + svelte-check (for the
/uisubpath). No runtime dependencies. Optional peer-deps:@lucide/svelte,@sveltejs/kit ^2,svelte ^5(only needed for the/uisubpath),typescript ^5 || ^6. - Runtime targets:
coreandserverare runtime-agnostic;opsneedsfetch; SvelteKit route-channel generation is a Node build-time tool. - Engines: Node
>=22
Commands
pnpm install
pnpm typecheck # tsc --noEmit (src + tests) + svelte-check on src/ui
pnpm test # vitest run
pnpm test:watch # vitest
pnpm test:coverage # vitest run --coverage
Architecture
src/
├── core.ts # barrel: types + viewmodel + builders + route channels
├── core/
│ ├── types.ts # RouteInventory, SitemapEntry, SitemapAudience, etc.
│ ├── viewmodel.ts # getFilteredSitemapGroups, getRouteTags, audience mapping
│ ├── builders.ts # createPageEntry, createApiEntry, createRouteInventory, …
│ └── channels.ts # host-owned route-channel policy validation + filtering
├── server.ts # barrel: XML + origin resolution
├── server/
│ └── xml.ts # buildSitemapXml, buildSitemapIndexXml, resolveSiteOrigin, escapeXml
├── ops.ts # barrel: pings + validation
├── ops/
│ ├── http.ts # @internal: fetchWithTimeout + retry helper
│ ├── ping.ts # pingSearchEngines + result types
│ └── validate.ts # validateSitemapUrls (URL HEAD-check)
├── sveltekit.ts # barrel: handlers + scanner + route-channel generation
├── sveltekit/
│ ├── channels.ts # check/generate channel-specific SvelteKit route trees
│ ├── handlers.ts # createSitemapXmlHandler + createRobotsTxtHandler
│ └── scanner.ts # scanSvelteKitRoutes (import.meta.glob → SitemapEntry[])
├── ui.ts # barrel: SitemapPage + CategoryMeta/Tone/SortOption types
├── ui/
│ ├── SitemapPage.svelte # themable Svelte 5 component
│ └── types.ts # CategoryTone, CategoryMeta, SortOption
├── svelte.d.ts # package-local ambient declaration for `*.svelte` imports
└── index.ts # barrel re-exporting core + server (NOT ops — fetch-coupled, NOT ui — Svelte-coupled)
package.json#exports points directly at ./src/*.ts. There is no build step. Consumers' bundlers (Vite/esbuild/SvelteKit) compile the .ts source as part of their own pipeline.
Every public factory in ops/* accepts an optional logger? and defaults to silent.
Code style
- Tabs, single quotes, no semicolons
- Strict TypeScript (
tsconfig.jsonenablesnoUncheckedIndexedAccess,noUnusedLocals, etc.) - All exports named; no default exports
- Pure functions in
coreandserver/xml— never reach forprocess,fetch, orglobalThisfrom those modules. Network-dependent code stays underops/.
Security rules (do not bypass)
escapeXmlMUST be applied to every value interpolated into a<loc>or<lastmod>element. The five replacements (&,<,>,",') are deliberate; do not narrow them.pingSearchEnginesMUST treat the caller-suppliedengines[]as authoritative — no implicit defaults. The historical Bing endpoint is exposed asHISTORICAL_PING_ENDPOINTSto be opted into explicitly; Google's ping endpoint was retired in 2023 and is intentionally not shipped.validateSitemapUrlsMUST take URLs directly (caller controls the target list) — never accept a database handle or a route scanner. SSRF prevention is the caller's responsibility, but the surface is shaped so the caller can enforce it.- When this package's deps change in
package.json, verify their licenses remain permissive (MIT / Apache 2.0 / BSD). No GPL-ish copyleft deps.
Package-vs-host boundary (the load-bearing design decision)
This package owns:
- Types describing a route inventory
- Pure filter/sort/visibility logic
- XML rendering (
sitemap.xmlandsitemap-index.xml) - Origin resolution from explicit baseUrl → request → fallback
- Search-engine ping orchestration (with retry + timeout)
- URL HEAD validation (with concurrency + bounded error output)
- Route-channel policy validation, filtering, and SvelteKit route-tree generation mechanics
The host application owns:
- The filesystem/glob inputs used for SvelteKit route discovery
- Route-channel names, route tags, private-directory policy, variants, and deployment wiring
- Category mapping (which paths belong to which group)
- Sitemap audience matchers (which paths are
public/internal/hidden) lastModifiedsource (git log, mtime, content store, etc.)- Page UI, brand copy, presentation
- Change-detection logic (e.g. "did anything publish since the last cron?") — too domain-specific to live here
- Stat aggregation that requires consumer DB queries — bring the numbers in, render them yourself
If a future capability needs DB access or business-domain knowledge, it belongs in the consumer, not here.
Where to look
- Public API barrel:
src/index.ts - Per-capability subpath:
src/<name>.ts(re-exports fromsrc/<name>/*.ts) - Route-channel mechanics:
src/core/channels.tsandsrc/sveltekit/channels.ts - UI component:
src/ui/SitemapPage.svelte; its types live next to it atsrc/ui/types.ts - Tests for each module:
tests/<name>.test.ts - Test config:
vitest.config.ts - Types-strict config:
tsconfig.json
Definition of Done
pnpm typecheckpasses with no errors (coverssrc/andtests/)pnpm testpasses with no failing assertions- Every entry in
package.json#exportspoints at an existingsrc/*.tsfile - No
dist/,node_modules/,.DS_Store, or*.tsbuildinfotracked - README + CHANGELOG updated for any user-facing change
- New deps reviewed for license compatibility (permissive only)
Shared-Folder Git
- Shared macOS/Linux checkouts should use
core.filemode=false; chmod-only changes will not be noticed reliably. - When a script must be executable, run
git update-index --chmod=+x path/to/script.shand include that in the commit.