@warlock.js/sitemap 5.19.1 → 5.20.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to `@warlock.js/sitemap` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.20.0 - 2026-09-24
8
+
9
+ ### Added
10
+
11
+ - `SitemapEntry.images` emits the Google image sitemap extension, validates
12
+ absolute HTTP(S) image URLs, and limits each URL to 1,000 images with a
13
+ caller-visible diagnostic.
14
+ - `SitemapIndexOptions.shardPathPrefix` controls public shard links in an
15
+ index without changing the files written by `saveTo()`.
16
+
17
+ ### Changed
18
+
19
+ - Refined grouped skill discovery guidance and regenerated package llms
20
+ projections.
21
+
7
22
  ## 5.19.0 - 2026-09-23
8
23
 
9
24
  ### Changed
package/cjs/index.cjs CHANGED
@@ -206,6 +206,7 @@ const CHANGE_FREQS = [
206
206
  "yearly",
207
207
  "never"
208
208
  ];
209
+ const MAX_IMAGES_PER_URL = 1e3;
209
210
  function assertChangeFreq(value) {
210
211
  if (!CHANGE_FREQS.includes(value)) throw new InvalidSitemapEntryError(`changefreq ${JSON.stringify(value)} is not one of ${CHANGE_FREQS.join(", ")}`);
211
212
  }
@@ -238,6 +239,22 @@ function normalizeEntry(entry, defaults) {
238
239
  path: normalizePath(alternate.path)
239
240
  };
240
241
  });
242
+ if (entry.images !== void 0 && !Array.isArray(entry.images)) throw new InvalidSitemapEntryError("images must be an array");
243
+ const images = entry.images?.slice(0, MAX_IMAGES_PER_URL).map((image) => {
244
+ if (typeof image?.loc !== "string" || image.loc.trim() === "") throw new InvalidSitemapEntryError("image loc is required");
245
+ let url;
246
+ try {
247
+ url = new URL(image.loc);
248
+ } catch {
249
+ throw new InvalidSitemapEntryError("image loc must be an absolute HTTP(S) URL");
250
+ }
251
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new InvalidSitemapEntryError("image loc must be an absolute HTTP(S) URL");
252
+ return { loc: image.loc };
253
+ });
254
+ if ((entry.images?.length ?? 0) > MAX_IMAGES_PER_URL) defaults.onImageLimitExceeded?.({
255
+ ...entry.route === void 0 ? {} : { route: entry.route },
256
+ dropped: entry.images.length - MAX_IMAGES_PER_URL
257
+ });
241
258
  return {
242
259
  path,
243
260
  ...entry.name !== void 0 ? { name: entry.name } : {},
@@ -245,7 +262,8 @@ function normalizeEntry(entry, defaults) {
245
262
  ...lastmod !== void 0 ? { lastmod: formatLastmod(lastmod) } : {},
246
263
  ...changefreq !== void 0 ? { changefreq } : {},
247
264
  ...priority !== void 0 ? { priority } : {},
248
- ...alternates !== void 0 ? { alternates } : {}
265
+ ...alternates !== void 0 ? { alternates } : {},
266
+ ...images !== void 0 ? { images } : {}
249
267
  };
250
268
  }
251
269
 
@@ -301,12 +319,15 @@ function escapeXml(value) {
301
319
  return value.replace(/[&<>"']/g, (char) => XML_ESCAPES[char] ?? char);
302
320
  }
303
321
  const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
322
+ const IMAGE_NAMESPACE = "http://www.google.com/schemas/sitemap-image/1.1";
304
323
  /**
305
324
  * The `xmlns:xhtml` attribute exactly as it appears on `<urlset>`, including
306
325
  * its leading space. Exported so the shard writer can charge its byte length
307
326
  * against the ceiling without duplicating the string it measures.
308
327
  */
309
328
  const XHTML_NAMESPACE_ATTR = ` xmlns:xhtml="${XHTML_NAMESPACE}"`;
329
+ /** See {@link XHTML_NAMESPACE_ATTR}; measured by the shard writer before roll-over. */
330
+ const IMAGE_NAMESPACE_ATTR = ` xmlns:image="${IMAGE_NAMESPACE}"`;
310
331
  /**
311
332
  * Renders one `<url>` block. Exported so the streaming writer can measure the
312
333
  * exact bytes it is about to append before deciding whether the byte ceiling
@@ -322,6 +343,11 @@ function renderUrlBlock(entry, baseUrl) {
322
343
  const href = escapeXml(resolveAgainstBase(baseUrl, alternate.path));
323
344
  lines.push(` <xhtml:link rel="alternate" hreflang="${escapeXml(alternate.hreflang)}" href="${href}"/>`);
324
345
  }
346
+ for (const image of entry.images ?? []) {
347
+ lines.push(` <image:image>`);
348
+ lines.push(` <image:loc>${escapeXml(resolveAgainstBase(baseUrl, image.loc))}</image:loc>`);
349
+ lines.push(` </image:image>`);
350
+ }
325
351
  lines.push(` </url>`);
326
352
  return lines.join("\n");
327
353
  }
@@ -335,7 +361,9 @@ function renderUrlBlock(entry, baseUrl) {
335
361
  * alternate — an unused namespace on every single-language sitemap is noise.
336
362
  */
337
363
  function buildSitemapXml(entries, baseUrl) {
338
- const namespaces = entries.some((entry) => (entry.alternates?.length ?? 0) > 0) ? XHTML_NAMESPACE_ATTR : "";
364
+ const hasAlternates = entries.some((entry) => (entry.alternates?.length ?? 0) > 0);
365
+ const hasImages = entries.some((entry) => (entry.images?.length ?? 0) > 0);
366
+ const namespaces = `${hasAlternates ? XHTML_NAMESPACE_ATTR : ""}${hasImages ? IMAGE_NAMESPACE_ATTR : ""}`;
339
367
  const body = entries.map((entry) => renderUrlBlock(entry, baseUrl)).join("\n");
340
368
  return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"${namespaces}>\n` + (body.length > 0 ? `${body}\n` : "") + `</urlset>\n`;
341
369
  }
@@ -355,6 +383,8 @@ function buildSitemapXml(entries, baseUrl) {
355
383
  var Sitemap = class {
356
384
  baseUrl;
357
385
  defaults;
386
+ warnedImageRoutes = /* @__PURE__ */ new Set();
387
+ optionsImageReporter;
358
388
  /** Keyed by path: a duplicate `<loc>` makes the document invalid, so the later add wins. */
359
389
  entriesByPath = /* @__PURE__ */ new Map();
360
390
  /** Insertion order per path, so `duplicates()` can name every colliding source. */
@@ -363,12 +393,21 @@ var Sitemap = class {
363
393
  declaredRoutes = /* @__PURE__ */ new Set();
364
394
  constructor(options) {
365
395
  this.baseUrl = normalizeBaseUrl(options?.baseUrl);
396
+ this.optionsImageReporter = options.onImageLimitExceeded;
366
397
  this.defaults = {
367
398
  changefreq: options.changefreq,
368
399
  priority: options.priority,
369
- lastmod: options.lastmod
400
+ lastmod: options.lastmod,
401
+ onImageLimitExceeded: (event) => this.reportImageLimit(event)
370
402
  };
371
403
  }
404
+ reportImageLimit(event) {
405
+ const key = event.route ?? "unattributed";
406
+ if (this.warnedImageRoutes.has(key)) return;
407
+ this.warnedImageRoutes.add(key);
408
+ if (this.optionsImageReporter) this.optionsImageReporter(event);
409
+ else console.warn(`[warlock:sitemap] ${key} exceeded the 1,000-image limit; dropped ${event.dropped} image(s).`);
410
+ }
372
411
  add(entry) {
373
412
  const resolved = normalizeEntry(entry, this.defaults);
374
413
  this.entriesByPath.set(resolved.path, resolved);
@@ -467,6 +506,7 @@ const PROTOCOL_MAX_BYTES_PER_FILE = 50 * 1024 * 1024;
467
506
  /** Validates and folds in defaults, once, at the constructor — the same discipline as `Sitemap`. */
468
507
  function normalizeSitemapIndexOptions(options) {
469
508
  const baseUrl = normalizeBaseUrl(options?.baseUrl);
509
+ const shardPathPrefix = normalizeShardPathPrefix(options.shardPathPrefix);
470
510
  const maxUrlsPerFile = options.maxUrlsPerFile ?? 5e4;
471
511
  const maxBytesPerFile = options.maxBytesPerFile ?? 52428800;
472
512
  if (!Number.isInteger(maxUrlsPerFile) || maxUrlsPerFile < 1 || maxUrlsPerFile > 5e4) throw new RangeError(`maxUrlsPerFile must be an integer between 1 and the sitemaps.org ceiling of ${PROTOCOL_MAX_URLS_PER_FILE}, got ${JSON.stringify(maxUrlsPerFile)}.`);
@@ -475,16 +515,24 @@ function normalizeSitemapIndexOptions(options) {
475
515
  baseUrl,
476
516
  filePrefix: options.filePrefix ?? "sitemap",
477
517
  indexFileName: options.indexFileName ?? "sitemap_index.xml",
518
+ shardPathPrefix,
478
519
  gzip: options.gzip ?? false,
479
520
  maxUrlsPerFile,
480
521
  maxBytesPerFile,
481
522
  defaults: {
482
523
  changefreq: options.changefreq,
483
524
  priority: options.priority,
484
- lastmod: options.lastmod
525
+ lastmod: options.lastmod,
526
+ onImageLimitExceeded: options.onImageLimitExceeded
485
527
  }
486
528
  };
487
529
  }
530
+ function normalizeShardPathPrefix(value) {
531
+ if (value === void 0 || value === "" || value === "/") return "";
532
+ const prefix = value.replace(/^\//, "").replace(/\/$/, "");
533
+ if (prefix.length === 0 || prefix.split("/").some((segment) => !/^[A-Za-z0-9_-]+$/.test(segment))) throw new TypeError("shardPathPrefix must contain only safe URL path segments.");
534
+ return `${prefix}/`;
535
+ }
488
536
 
489
537
  //#endregion
490
538
  //#region ../sitemap/src/shard-name.ts
@@ -525,6 +573,7 @@ function envelopeBytes(baseUrl) {
525
573
  * shard's real bytes on disk exceed the ceiling it was rolled against.
526
574
  */
527
575
  const NAMESPACE_BYTES = Buffer.byteLength(XHTML_NAMESPACE_ATTR, "utf8");
576
+ const IMAGE_NAMESPACE_BYTES = Buffer.byteLength(IMAGE_NAMESPACE_ATTR, "utf8");
528
577
  async function writeShardFile(ctx, fileName, entries) {
529
578
  const xml = buildSitemapXml(entries, ctx.baseUrl);
530
579
  const filePath = (0, node_path.join)(ctx.tempDir, fileName);
@@ -546,6 +595,7 @@ async function writeShardGroup(group, ctx) {
546
595
  let buffer = [];
547
596
  let bufferBytes = envelopeBytes(ctx.baseUrl);
548
597
  let bufferHasAlternates = false;
598
+ let bufferHasImages = false;
549
599
  let ordinal = 1;
550
600
  const flush = async () => {
551
601
  if (buffer.length === 0) return;
@@ -562,6 +612,7 @@ async function writeShardGroup(group, ctx) {
562
612
  buffer = [];
563
613
  bufferBytes = envelopeBytes(ctx.baseUrl);
564
614
  bufferHasAlternates = false;
615
+ bufferHasImages = false;
565
616
  };
566
617
  for (const factory of group.factories) {
567
618
  const source = await factory();
@@ -569,15 +620,18 @@ async function writeShardGroup(group, ctx) {
569
620
  const resolved = normalizeEntry(rawEntry, ctx.defaults);
570
621
  if (!ctx.duplicates.attempt(resolved.path, resolved.route)) continue;
571
622
  const entryHasAlternates = (resolved.alternates?.length ?? 0) > 0;
623
+ const entryHasImages = (resolved.images?.length ?? 0) > 0;
572
624
  const blockBytes = Buffer.byteLength(renderUrlBlock(resolved, ctx.baseUrl), "utf8") + 1;
573
- const addedBytes = blockBytes + (entryHasAlternates && !bufferHasAlternates ? NAMESPACE_BYTES : 0);
625
+ const addedBytes = blockBytes + (entryHasAlternates && !bufferHasAlternates ? NAMESPACE_BYTES : 0) + (entryHasImages && !bufferHasImages ? IMAGE_NAMESPACE_BYTES : 0);
574
626
  const hitsUrlCeiling = buffer.length >= ctx.maxUrlsPerFile;
575
627
  const hitsByteCeiling = buffer.length > 0 && bufferBytes + addedBytes > ctx.maxBytesPerFile;
576
628
  if (hitsUrlCeiling || hitsByteCeiling) await flush();
577
629
  const addsNamespaceNow = entryHasAlternates && !bufferHasAlternates;
630
+ const addsImageNamespaceNow = entryHasImages && !bufferHasImages;
578
631
  buffer.push(resolved);
579
- bufferBytes += blockBytes + (addsNamespaceNow ? NAMESPACE_BYTES : 0);
632
+ bufferBytes += blockBytes + (addsNamespaceNow ? NAMESPACE_BYTES : 0) + (addsImageNamespaceNow ? IMAGE_NAMESPACE_BYTES : 0);
580
633
  if (addsNamespaceNow) bufferHasAlternates = true;
634
+ if (addsImageNamespaceNow) bufferHasImages = true;
581
635
  ctx.routes.record(resolved.route);
582
636
  }
583
637
  }
@@ -600,9 +654,9 @@ async function writeShardGroup(group, ctx) {
600
654
  * nested per-group indexes, and never a zero-url row (that is a diagnostic
601
655
  * for `files`, not something a crawler should be told to fetch).
602
656
  */
603
- function buildSitemapIndexXml(files, baseUrl) {
657
+ function buildSitemapIndexXml(files, baseUrl, shardPathPrefix = "") {
604
658
  const body = files.filter((file) => file.urls > 0).map((file) => {
605
- return ` <sitemap>\n <loc>${escapeXml(joinOrigin(baseUrl, file.fileName))}</loc>\n </sitemap>`;
659
+ return ` <sitemap>\n <loc>${escapeXml(joinOrigin(baseUrl, `${shardPathPrefix}${file.fileName}`))}</loc>\n </sitemap>`;
606
660
  }).join("\n");
607
661
  return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" + (body.length > 0 ? `${body}\n` : "") + `</sitemapindex>\n`;
608
662
  }
@@ -674,6 +728,7 @@ var SitemapIndex = class {
674
728
  namedGroups = [];
675
729
  /** Canonical (lowercased) keys already registered, so a case collision is caught at `addSource()`. */
676
730
  registeredKeys = /* @__PURE__ */ new Set();
731
+ warnedImageRoutes = /* @__PURE__ */ new Set();
677
732
  constructor(options) {
678
733
  this.options = normalizeSitemapIndexOptions(options);
679
734
  }
@@ -713,13 +768,16 @@ var SitemapIndex = class {
713
768
  gzip: this.options.gzip,
714
769
  maxUrlsPerFile: this.options.maxUrlsPerFile,
715
770
  maxBytesPerFile: this.options.maxBytesPerFile,
716
- defaults: this.options.defaults,
771
+ defaults: {
772
+ ...this.options.defaults,
773
+ onImageLimitExceeded: (event) => this.reportImageLimit(event)
774
+ },
717
775
  duplicates,
718
776
  routes
719
777
  });
720
778
  shardFiles.push(...files);
721
779
  }
722
- const indexXml = buildSitemapIndexXml(shardFiles, this.options.baseUrl);
780
+ const indexXml = buildSitemapIndexXml(shardFiles, this.options.baseUrl, this.options.shardPathPrefix);
723
781
  await (0, node_fs_promises.writeFile)((0, node_path.join)(tempDir, this.options.indexFileName), indexXml, "utf8");
724
782
  return [...shardFiles.filter((file) => file.urls > 0).map((file) => file.fileName), this.options.indexFileName];
725
783
  };
@@ -739,17 +797,143 @@ var SitemapIndex = class {
739
797
  routes: routes.summary()
740
798
  };
741
799
  }
800
+ reportImageLimit(event) {
801
+ const key = event.route ?? "unattributed";
802
+ if (this.warnedImageRoutes.has(key)) return;
803
+ this.warnedImageRoutes.add(key);
804
+ if (this.options.defaults.onImageLimitExceeded) this.options.defaults.onImageLimitExceeded(event);
805
+ else console.warn(`[warlock:sitemap] ${key} exceeded the 1,000-image limit; dropped ${event.dropped} image(s).`);
806
+ }
807
+ };
808
+
809
+ //#endregion
810
+ //#region ../sitemap/src/generation-manifest.ts
811
+ /** Storage-independent schema and key helpers for immutable sitemap generations. */
812
+ const GENERATION_MANIFEST_VERSION = 1;
813
+ const MANIFEST_FENCE_WIDTH = 16;
814
+ var InvalidSitemapGenerationManifestError = class extends Error {
815
+ constructor(message) {
816
+ super(`Invalid sitemap generation manifest: ${message}`);
817
+ this.name = "InvalidSitemapGenerationManifestError";
818
+ }
742
819
  };
820
+ const SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
821
+ const SAFE_FILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/;
822
+ const SHA256 = /^[a-f0-9]{64}$/;
823
+ const ISO_DATE_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
824
+ const MANIFEST_KEY = /^manifests\/(\d{16})-([A-Za-z0-9][A-Za-z0-9_-]{0,127})\.json$/;
825
+ function fail(message) {
826
+ throw new InvalidSitemapGenerationManifestError(message);
827
+ }
828
+ function object(value, label) {
829
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`);
830
+ return value;
831
+ }
832
+ function nonNegativeInteger(value, label) {
833
+ if (!Number.isSafeInteger(value) || value < 0) return fail(`${label} must be a safe non-negative integer.`);
834
+ return value;
835
+ }
836
+ function generationIdentifier(value, label) {
837
+ if (typeof value !== "string" || !SAFE_IDENTIFIER.test(value)) return fail(`${label} must be a safe generation identifier.`);
838
+ return value;
839
+ }
840
+ function generatedAt(value) {
841
+ if (typeof value !== "string" || !ISO_DATE_TIME.test(value) || Number.isNaN(Date.parse(value))) return fail("generatedAt must be a valid ISO-8601 date-time.");
842
+ return value;
843
+ }
844
+ function artifactPath(value, generationId, label) {
845
+ if (typeof value !== "string") return fail(`${label} must be a string.`);
846
+ const prefix = `generations/${generationId}/`;
847
+ if (!value.startsWith(prefix)) return fail(`${label} must be contained in ${prefix}.`);
848
+ const fileName = value.slice(prefix.length);
849
+ if (!SAFE_FILE_NAME.test(fileName) || fileName.includes("/") || fileName === "." || fileName === "..") return fail(`${label} must name one safe, flat artifact file.`);
850
+ return value;
851
+ }
852
+ function manifestFile(value, generationId, index) {
853
+ const record = object(value, `files[${index}]`);
854
+ const path = artifactPath(record.path, generationId, `files[${index}].path`);
855
+ const bytes = nonNegativeInteger(record.bytes, `files[${index}].bytes`);
856
+ if (typeof record.sha256 !== "string" || !SHA256.test(record.sha256)) return fail(`files[${index}].sha256 must be a lowercase SHA-256 hex digest.`);
857
+ return {
858
+ path,
859
+ bytes,
860
+ sha256: record.sha256
861
+ };
862
+ }
863
+ /** Parse untrusted JSON into the v1 manifest contract. It does not touch storage. */
864
+ function parseSitemapGenerationManifest(value) {
865
+ const record = object(value, "manifest");
866
+ if (record.version !== 1) return fail(`version must be ${1}.`);
867
+ const fence = nonNegativeInteger(record.fence, "fence");
868
+ const generationId = generationIdentifier(record.generationId, "generationId");
869
+ const coversRev = nonNegativeInteger(record.coversRev, "coversRev");
870
+ if (record.kind !== "single" && record.kind !== "index") return fail("kind must be \"single\" or \"index\".");
871
+ if (!Array.isArray(record.files) || record.files.length === 0) return fail("files must be a non-empty array.");
872
+ const files = record.files.map((file, index) => manifestFile(file, generationId, index));
873
+ const paths = /* @__PURE__ */ new Set();
874
+ const fileNames = /* @__PURE__ */ new Set();
875
+ for (const file of files) {
876
+ const fileName = file.path.slice(`generations/${generationId}/`.length);
877
+ if (paths.has(file.path)) return fail(`files contains duplicate path ${JSON.stringify(file.path)}.`);
878
+ if (fileNames.has(fileName)) return fail(`files contains duplicate public file name ${JSON.stringify(fileName)}.`);
879
+ paths.add(file.path);
880
+ fileNames.add(fileName);
881
+ }
882
+ const mainFile = artifactPath(record.mainFile, generationId, "mainFile");
883
+ if (!paths.has(mainFile)) return fail("mainFile must match one files[].path.");
884
+ return {
885
+ version: 1,
886
+ fence,
887
+ generationId,
888
+ coversRev,
889
+ kind: record.kind,
890
+ mainFile,
891
+ files,
892
+ entries: nonNegativeInteger(record.entries, "entries"),
893
+ generatedAt: generatedAt(record.generatedAt)
894
+ };
895
+ }
896
+ /** Build the immutable v1 manifest object key for a generation fence. */
897
+ function sitemapManifestKey(fence, generationId) {
898
+ const safeFence = nonNegativeInteger(fence, "fence");
899
+ if (safeFence >= 10 ** 16) return fail(`fence must fit within ${16} decimal digits.`);
900
+ return `manifests/${String(safeFence).padStart(16, "0")}-${generationIdentifier(generationId, "generationId")}.json`;
901
+ }
902
+ /** Parse only the shape of a manifest object key; artifact validation belongs to the storage adapter. */
903
+ function parseSitemapManifestKey(key) {
904
+ if (typeof key !== "string") return void 0;
905
+ const match = MANIFEST_KEY.exec(key);
906
+ if (!match) return void 0;
907
+ const fence = Number(match[1]);
908
+ const generationId = match[2];
909
+ if (!Number.isSafeInteger(fence) || generationId === void 0) return void 0;
910
+ return {
911
+ key,
912
+ fence,
913
+ generationId
914
+ };
915
+ }
916
+ /** Numeric-fence-descending candidates; callers validate storage contents before adoption. */
917
+ function sortSitemapManifestCandidates(keys) {
918
+ return keys.map(parseSitemapManifestKey).filter((candidate) => candidate !== void 0).sort((left, right) => right.fence - left.fence || right.generationId.localeCompare(left.generationId));
919
+ }
743
920
 
744
921
  //#endregion
745
922
  exports.DuplicateSourceKeyError = DuplicateSourceKeyError;
923
+ exports.GENERATION_MANIFEST_VERSION = GENERATION_MANIFEST_VERSION;
746
924
  exports.InvalidBaseUrlError = InvalidBaseUrlError;
747
925
  exports.InvalidSitemapEntryError = InvalidSitemapEntryError;
926
+ exports.InvalidSitemapGenerationManifestError = InvalidSitemapGenerationManifestError;
927
+ exports.MANIFEST_FENCE_WIDTH = MANIFEST_FENCE_WIDTH;
748
928
  exports.Sitemap = Sitemap;
749
929
  exports.SitemapIndex = SitemapIndex;
750
930
  exports.UnownedOutputDirectoryError = UnownedOutputDirectoryError;
751
931
  exports.buildSitemapXml = buildSitemapXml;
752
932
  exports.escapeXml = escapeXml;
753
933
  exports.joinOrigin = joinOrigin;
934
+ exports.parseSitemapGenerationManifest = parseSitemapGenerationManifest;
935
+ exports.parseSitemapManifestKey = parseSitemapManifestKey;
754
936
  exports.renderUrlBlock = renderUrlBlock;
937
+ exports.sitemapManifestKey = sitemapManifestKey;
938
+ exports.sortSitemapManifestCandidates = sortSitemapManifestCandidates;
755
939
  //# sourceMappingURL=index.cjs.map