@nuxtjs/sitemap 7.4.4 → 7.4.5
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/package.json +3 -3
- package/dist/client/200.html +0 -1
- package/dist/client/404.html +0 -1
- package/dist/client/__sitemap__/style.xsl +0 -190
- package/dist/client/_nuxt/BRTu1kzH.js +0 -172
- package/dist/client/_nuxt/CVMmb_pX.js +0 -1
- package/dist/client/_nuxt/CVO1_9PV.js +0 -1
- package/dist/client/_nuxt/Cp-IABpG.js +0 -1
- package/dist/client/_nuxt/D0r3Knsf.js +0 -1
- package/dist/client/_nuxt/builds/latest.json +0 -1
- package/dist/client/_nuxt/builds/meta/7faab852-f8a4-43e6-a124-2cd93df90673.json +0 -1
- package/dist/client/_nuxt/entry.D5V0t8Hh.css +0 -1
- package/dist/client/_nuxt/error-404.BXx3NK2Z.css +0 -1
- package/dist/client/_nuxt/error-500.BYrbHUTO.css +0 -1
- package/dist/client/_nuxt/rHMUQZkV.js +0 -1
- package/dist/client/index.html +0 -1
- package/dist/client/sitemap.xml +0 -7
- package/dist/content.d.mts +0 -232
- package/dist/content.d.ts +0 -232
- package/dist/content.mjs +0 -45
- package/dist/module.d.mts +0 -10
- package/dist/module.d.ts +0 -10
- package/dist/module.json +0 -12
- package/dist/module.mjs +0 -1217
- package/dist/runtime/server/composables/asSitemapUrl.d.ts +0 -2
- package/dist/runtime/server/composables/asSitemapUrl.js +0 -3
- package/dist/runtime/server/composables/defineSitemapEventHandler.d.ts +0 -4
- package/dist/runtime/server/composables/defineSitemapEventHandler.js +0 -2
- package/dist/runtime/server/content-compat.d.ts +0 -1
- package/dist/runtime/server/content-compat.js +0 -2
- package/dist/runtime/server/kit.d.ts +0 -3
- package/dist/runtime/server/kit.js +0 -25
- package/dist/runtime/server/plugins/compression.d.ts +0 -2
- package/dist/runtime/server/plugins/compression.js +0 -8
- package/dist/runtime/server/plugins/nuxt-content-v2.d.ts +0 -2
- package/dist/runtime/server/plugins/nuxt-content-v2.js +0 -39
- package/dist/runtime/server/plugins/warm-up.d.ts +0 -2
- package/dist/runtime/server/plugins/warm-up.js +0 -39
- package/dist/runtime/server/robots-polyfill/getPathRobotConfig.d.ts +0 -5
- package/dist/runtime/server/robots-polyfill/getPathRobotConfig.js +0 -3
- package/dist/runtime/server/routes/__sitemap__/debug.d.ts +0 -40
- package/dist/runtime/server/routes/__sitemap__/debug.js +0 -29
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.d.ts +0 -2
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.js +0 -6
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.d.ts +0 -2
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +0 -24
- package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +0 -2
- package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +0 -50
- package/dist/runtime/server/routes/sitemap.xml.d.ts +0 -2
- package/dist/runtime/server/routes/sitemap.xml.js +0 -13
- package/dist/runtime/server/routes/sitemap.xsl.d.ts +0 -2
- package/dist/runtime/server/routes/sitemap.xsl.js +0 -255
- package/dist/runtime/server/routes/sitemap_index.xml.d.ts +0 -2
- package/dist/runtime/server/routes/sitemap_index.xml.js +0 -42
- package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +0 -13
- package/dist/runtime/server/sitemap/builder/sitemap-index.js +0 -197
- package/dist/runtime/server/sitemap/builder/sitemap.d.ts +0 -16
- package/dist/runtime/server/sitemap/builder/sitemap.js +0 -241
- package/dist/runtime/server/sitemap/builder/xml.d.ts +0 -6
- package/dist/runtime/server/sitemap/builder/xml.js +0 -197
- package/dist/runtime/server/sitemap/nitro.d.ts +0 -4
- package/dist/runtime/server/sitemap/nitro.js +0 -144
- package/dist/runtime/server/sitemap/urlset/normalise.d.ts +0 -6
- package/dist/runtime/server/sitemap/urlset/normalise.js +0 -137
- package/dist/runtime/server/sitemap/urlset/sort.d.ts +0 -2
- package/dist/runtime/server/sitemap/urlset/sort.js +0 -13
- package/dist/runtime/server/sitemap/urlset/sources.d.ts +0 -6
- package/dist/runtime/server/sitemap/urlset/sources.js +0 -143
- package/dist/runtime/server/sitemap/utils/chunk.d.ts +0 -10
- package/dist/runtime/server/sitemap/utils/chunk.js +0 -67
- package/dist/runtime/server/tsconfig.json +0 -3
- package/dist/runtime/server/utils.d.ts +0 -5
- package/dist/runtime/server/utils.js +0 -16
- package/dist/runtime/types.d.ts +0 -458
- package/dist/runtime/types.js +0 -0
- package/dist/runtime/utils-pure.d.ts +0 -14
- package/dist/runtime/utils-pure.js +0 -85
- package/dist/shared/sitemap.DR3_6qqU.mjs +0 -212
- package/dist/types.d.mts +0 -5
- package/dist/utils.d.mts +0 -28
- package/dist/utils.d.ts +0 -28
- package/dist/utils.mjs +0 -368
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { parseURL } from 'ufo';
|
|
2
|
-
import { parse, walkSync, ELEMENT_NODE } from 'ultrahtml';
|
|
3
|
-
|
|
4
|
-
function isValidUrl(url) {
|
|
5
|
-
if (!url || typeof url !== "string") return false;
|
|
6
|
-
const trimmed = url.trim();
|
|
7
|
-
if (!trimmed) return false;
|
|
8
|
-
if (trimmed.startsWith("data:") || trimmed.startsWith("blob:") || trimmed.startsWith("file:")) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const parsed = parseURL(trimmed);
|
|
13
|
-
return !!(parsed.protocol && parsed.host) || !!parsed.pathname;
|
|
14
|
-
} catch {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function isValidString(value) {
|
|
19
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
20
|
-
}
|
|
21
|
-
function sanitizeString(value) {
|
|
22
|
-
if (!isValidString(value)) return "";
|
|
23
|
-
return String(value).trim().replace(/[\x00-\x1F\x7F-\x9F]/g, "");
|
|
24
|
-
}
|
|
25
|
-
function isValidDate(dateString) {
|
|
26
|
-
if (!dateString) return false;
|
|
27
|
-
const date = new Date(dateString);
|
|
28
|
-
return !Number.isNaN(date.getTime()) && date.getFullYear() > 1900 && date.getFullYear() < 3e3;
|
|
29
|
-
}
|
|
30
|
-
function parseHtmlExtractSitemapMeta(html, options) {
|
|
31
|
-
options = options || { images: true, videos: true, lastmod: true, alternatives: true };
|
|
32
|
-
const payload = {};
|
|
33
|
-
const resolveUrl = options?.resolveUrl || ((s) => s);
|
|
34
|
-
let doc;
|
|
35
|
-
try {
|
|
36
|
-
doc = parse(html);
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.warn("Failed to parse HTML:", error);
|
|
39
|
-
return payload;
|
|
40
|
-
}
|
|
41
|
-
let mainElement = null;
|
|
42
|
-
const images = /* @__PURE__ */ new Set();
|
|
43
|
-
const videos = [];
|
|
44
|
-
const videoSources = /* @__PURE__ */ new Map();
|
|
45
|
-
let articleModifiedTime;
|
|
46
|
-
const alternatives = [];
|
|
47
|
-
walkSync(doc, (node) => {
|
|
48
|
-
if (node.type === ELEMENT_NODE) {
|
|
49
|
-
const element = node;
|
|
50
|
-
const attrs = element.attributes || {};
|
|
51
|
-
if (element.name === "main" && !mainElement) {
|
|
52
|
-
mainElement = element;
|
|
53
|
-
}
|
|
54
|
-
if (options?.lastmod && element.name === "meta") {
|
|
55
|
-
const property = sanitizeString(attrs.property);
|
|
56
|
-
const content = sanitizeString(attrs.content);
|
|
57
|
-
if (property === "article:modified_time" && content && isValidDate(content)) {
|
|
58
|
-
articleModifiedTime = content;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (options?.alternatives && element.name === "link") {
|
|
62
|
-
const rel = sanitizeString(attrs.rel);
|
|
63
|
-
const href = sanitizeString(attrs.href);
|
|
64
|
-
const hreflang = sanitizeString(attrs.hreflang);
|
|
65
|
-
if (rel === "alternate" && href && hreflang && isValidUrl(href)) {
|
|
66
|
-
const hreflangPattern = /^[a-z]{2}(?:-[A-Z]{2})?$|^x-default$/;
|
|
67
|
-
if (hreflangPattern.test(hreflang)) {
|
|
68
|
-
try {
|
|
69
|
-
const parsed = parseURL(href);
|
|
70
|
-
if (parsed.pathname) {
|
|
71
|
-
alternatives.push({
|
|
72
|
-
hreflang,
|
|
73
|
-
href: parsed.pathname
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
const searchScope = mainElement || doc;
|
|
84
|
-
walkSync(searchScope, (node) => {
|
|
85
|
-
if (node.type === ELEMENT_NODE) {
|
|
86
|
-
const element = node;
|
|
87
|
-
const attrs = element.attributes || {};
|
|
88
|
-
if (options?.images && element.name === "img") {
|
|
89
|
-
const src = sanitizeString(attrs.src);
|
|
90
|
-
if (src && isValidUrl(src)) {
|
|
91
|
-
const resolvedUrl = resolveUrl(src);
|
|
92
|
-
if (isValidUrl(resolvedUrl)) {
|
|
93
|
-
images.add(resolvedUrl);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (options?.videos && element.name === "video") {
|
|
98
|
-
const content_loc = sanitizeString(attrs.src);
|
|
99
|
-
const thumbnail_loc = sanitizeString(attrs.poster);
|
|
100
|
-
const title = sanitizeString(attrs["data-title"]);
|
|
101
|
-
const description = sanitizeString(attrs["data-description"]);
|
|
102
|
-
if (!title || !description) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const videoObj = {
|
|
106
|
-
content_loc,
|
|
107
|
-
thumbnail_loc,
|
|
108
|
-
title,
|
|
109
|
-
description
|
|
110
|
-
};
|
|
111
|
-
const player_loc = sanitizeString(attrs["data-player-loc"]);
|
|
112
|
-
if (player_loc && isValidUrl(player_loc)) {
|
|
113
|
-
videoObj.player_loc = player_loc;
|
|
114
|
-
}
|
|
115
|
-
const duration = sanitizeString(attrs["data-duration"]);
|
|
116
|
-
if (duration) {
|
|
117
|
-
const parsedDuration = Number.parseInt(duration, 10);
|
|
118
|
-
if (!Number.isNaN(parsedDuration) && parsedDuration > 0 && parsedDuration <= 28800) {
|
|
119
|
-
videoObj.duration = parsedDuration;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const expiration_date = sanitizeString(attrs["data-expiration-date"]);
|
|
123
|
-
if (expiration_date && isValidDate(expiration_date)) {
|
|
124
|
-
videoObj.expiration_date = expiration_date;
|
|
125
|
-
}
|
|
126
|
-
const rating = sanitizeString(attrs["data-rating"]);
|
|
127
|
-
if (rating) {
|
|
128
|
-
const parsedRating = Number.parseFloat(rating);
|
|
129
|
-
if (!Number.isNaN(parsedRating) && parsedRating >= 0 && parsedRating <= 5) {
|
|
130
|
-
videoObj.rating = parsedRating;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
const view_count = sanitizeString(attrs["data-view-count"]);
|
|
134
|
-
if (view_count) {
|
|
135
|
-
const parsedViewCount = Number.parseInt(view_count, 10);
|
|
136
|
-
if (!Number.isNaN(parsedViewCount) && parsedViewCount >= 0) {
|
|
137
|
-
videoObj.view_count = parsedViewCount;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const publication_date = sanitizeString(attrs["data-publication-date"]);
|
|
141
|
-
if (publication_date && isValidDate(publication_date)) {
|
|
142
|
-
videoObj.publication_date = publication_date;
|
|
143
|
-
}
|
|
144
|
-
const family_friendly = sanitizeString(attrs["data-family-friendly"]);
|
|
145
|
-
if (family_friendly && ["yes", "no"].includes(family_friendly.toLowerCase())) {
|
|
146
|
-
videoObj.family_friendly = family_friendly.toLowerCase();
|
|
147
|
-
}
|
|
148
|
-
const requires_subscription = sanitizeString(attrs["data-requires-subscription"]);
|
|
149
|
-
if (requires_subscription && ["yes", "no"].includes(requires_subscription.toLowerCase())) {
|
|
150
|
-
videoObj.requires_subscription = requires_subscription.toLowerCase();
|
|
151
|
-
}
|
|
152
|
-
const live = sanitizeString(attrs["data-live"]);
|
|
153
|
-
if (live && ["yes", "no"].includes(live.toLowerCase())) {
|
|
154
|
-
videoObj.live = live.toLowerCase();
|
|
155
|
-
}
|
|
156
|
-
const tag = sanitizeString(attrs["data-tag"]);
|
|
157
|
-
if (tag && tag.length <= 256) {
|
|
158
|
-
videoObj.tag = tag;
|
|
159
|
-
}
|
|
160
|
-
videos.push({ videoObj, element });
|
|
161
|
-
}
|
|
162
|
-
if (options?.videos && element.name === "source" && element.parent && element.parent.name === "video") {
|
|
163
|
-
const videoElement = element.parent;
|
|
164
|
-
const src = sanitizeString(attrs.src);
|
|
165
|
-
if (src && isValidUrl(src)) {
|
|
166
|
-
if (!videoSources.has(videoElement)) {
|
|
167
|
-
videoSources.set(videoElement, []);
|
|
168
|
-
}
|
|
169
|
-
videoSources.get(videoElement).push(src);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
if (options?.images && images.size > 0) {
|
|
175
|
-
payload.images = [...images].map((i) => ({ loc: i }));
|
|
176
|
-
}
|
|
177
|
-
if (options?.videos) {
|
|
178
|
-
const processedVideos = [];
|
|
179
|
-
for (const { videoObj, element } of videos) {
|
|
180
|
-
const sources = videoSources.get(element) || [];
|
|
181
|
-
if (sources.length > 0) {
|
|
182
|
-
for (const source of sources) {
|
|
183
|
-
const resolvedVideoObj = { ...videoObj };
|
|
184
|
-
if (resolvedVideoObj.thumbnail_loc) {
|
|
185
|
-
resolvedVideoObj.thumbnail_loc = resolveUrl(String(resolvedVideoObj.thumbnail_loc));
|
|
186
|
-
}
|
|
187
|
-
processedVideos.push({
|
|
188
|
-
...resolvedVideoObj,
|
|
189
|
-
content_loc: resolveUrl(source)
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
} else {
|
|
193
|
-
processedVideos.push(videoObj);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const validVideos = processedVideos.filter((v) => {
|
|
197
|
-
return isValidString(v.title) && isValidString(v.description) && isValidString(v.content_loc) && isValidUrl(v.content_loc) && isValidString(v.thumbnail_loc) && isValidUrl(v.thumbnail_loc) && v.title.length <= 2048 && v.description.length <= 2048;
|
|
198
|
-
});
|
|
199
|
-
if (validVideos.length > 0) {
|
|
200
|
-
payload.videos = validVideos;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
if (options?.lastmod && articleModifiedTime) {
|
|
204
|
-
payload.lastmod = articleModifiedTime;
|
|
205
|
-
}
|
|
206
|
-
if (options?.alternatives && alternatives.length > 0 && (alternatives.length > 1 || alternatives[0].hreflang !== "x-default")) {
|
|
207
|
-
payload.alternatives = alternatives;
|
|
208
|
-
}
|
|
209
|
-
return payload;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export { parseHtmlExtractSitemapMeta as p };
|
package/dist/types.d.mts
DELETED
package/dist/utils.d.mts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { SitemapUrlInput, SitemapUrl } from '../dist/runtime/types.js';
|
|
2
|
-
export * from '../dist/runtime/types.js';
|
|
3
|
-
|
|
4
|
-
interface SitemapWarning {
|
|
5
|
-
type: 'validation';
|
|
6
|
-
message: string;
|
|
7
|
-
context?: {
|
|
8
|
-
url?: string;
|
|
9
|
-
field?: string;
|
|
10
|
-
value?: unknown;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
interface SitemapParseResult {
|
|
14
|
-
urls: SitemapUrlInput[];
|
|
15
|
-
warnings: SitemapWarning[];
|
|
16
|
-
}
|
|
17
|
-
declare function parseSitemapXml(xml: string): Promise<SitemapParseResult>;
|
|
18
|
-
|
|
19
|
-
declare function parseHtmlExtractSitemapMeta(html: string, options?: {
|
|
20
|
-
images?: boolean;
|
|
21
|
-
videos?: boolean;
|
|
22
|
-
lastmod?: boolean;
|
|
23
|
-
alternatives?: boolean;
|
|
24
|
-
resolveUrl?: (s: string) => string;
|
|
25
|
-
}): Partial<SitemapUrl>;
|
|
26
|
-
|
|
27
|
-
export { parseHtmlExtractSitemapMeta, parseSitemapXml };
|
|
28
|
-
export type { SitemapParseResult, SitemapWarning };
|
package/dist/utils.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { SitemapUrlInput, SitemapUrl } from '../dist/runtime/types.js';
|
|
2
|
-
export * from '../dist/runtime/types.js';
|
|
3
|
-
|
|
4
|
-
interface SitemapWarning {
|
|
5
|
-
type: 'validation';
|
|
6
|
-
message: string;
|
|
7
|
-
context?: {
|
|
8
|
-
url?: string;
|
|
9
|
-
field?: string;
|
|
10
|
-
value?: unknown;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
interface SitemapParseResult {
|
|
14
|
-
urls: SitemapUrlInput[];
|
|
15
|
-
warnings: SitemapWarning[];
|
|
16
|
-
}
|
|
17
|
-
declare function parseSitemapXml(xml: string): Promise<SitemapParseResult>;
|
|
18
|
-
|
|
19
|
-
declare function parseHtmlExtractSitemapMeta(html: string, options?: {
|
|
20
|
-
images?: boolean;
|
|
21
|
-
videos?: boolean;
|
|
22
|
-
lastmod?: boolean;
|
|
23
|
-
alternatives?: boolean;
|
|
24
|
-
resolveUrl?: (s: string) => string;
|
|
25
|
-
}): Partial<SitemapUrl>;
|
|
26
|
-
|
|
27
|
-
export { parseHtmlExtractSitemapMeta, parseSitemapXml };
|
|
28
|
-
export type { SitemapParseResult, SitemapWarning };
|
package/dist/utils.mjs
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
export { p as parseHtmlExtractSitemapMeta } from './shared/sitemap.DR3_6qqU.mjs';
|
|
2
|
-
import 'ufo';
|
|
3
|
-
import 'ultrahtml';
|
|
4
|
-
|
|
5
|
-
function isValidString(value) {
|
|
6
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
7
|
-
}
|
|
8
|
-
function parseNumber(value) {
|
|
9
|
-
if (typeof value === "number") return value;
|
|
10
|
-
if (typeof value === "string" && value.trim()) {
|
|
11
|
-
const num = Number.parseFloat(value.trim());
|
|
12
|
-
return Number.isNaN(num) ? void 0 : num;
|
|
13
|
-
}
|
|
14
|
-
return void 0;
|
|
15
|
-
}
|
|
16
|
-
function parseInteger(value) {
|
|
17
|
-
if (typeof value === "number") return Math.floor(value);
|
|
18
|
-
if (typeof value === "string" && value.trim()) {
|
|
19
|
-
const num = Number.parseInt(value.trim(), 10);
|
|
20
|
-
return Number.isNaN(num) ? void 0 : num;
|
|
21
|
-
}
|
|
22
|
-
return void 0;
|
|
23
|
-
}
|
|
24
|
-
function extractUrlFromParsedElement(urlElement, warnings) {
|
|
25
|
-
if (!isValidString(urlElement.loc)) {
|
|
26
|
-
warnings.push({
|
|
27
|
-
type: "validation",
|
|
28
|
-
message: "URL entry missing required loc element",
|
|
29
|
-
context: { url: String(urlElement.loc || "undefined") }
|
|
30
|
-
});
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
const urlObj = { loc: urlElement.loc };
|
|
34
|
-
if (isValidString(urlElement.lastmod)) {
|
|
35
|
-
urlObj.lastmod = urlElement.lastmod;
|
|
36
|
-
}
|
|
37
|
-
if (isValidString(urlElement.changefreq)) {
|
|
38
|
-
const validFreqs = ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"];
|
|
39
|
-
if (validFreqs.includes(urlElement.changefreq)) {
|
|
40
|
-
urlObj.changefreq = urlElement.changefreq;
|
|
41
|
-
} else {
|
|
42
|
-
warnings.push({
|
|
43
|
-
type: "validation",
|
|
44
|
-
message: "Invalid changefreq value",
|
|
45
|
-
context: { url: urlElement.loc, field: "changefreq", value: urlElement.changefreq }
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const priority = parseNumber(urlElement.priority);
|
|
50
|
-
if (priority !== void 0 && !Number.isNaN(priority)) {
|
|
51
|
-
if (priority < 0 || priority > 1) {
|
|
52
|
-
warnings.push({
|
|
53
|
-
type: "validation",
|
|
54
|
-
message: "Priority value should be between 0.0 and 1.0, clamping to valid range",
|
|
55
|
-
context: { url: urlElement.loc, field: "priority", value: priority }
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
urlObj.priority = Math.max(0, Math.min(1, priority));
|
|
59
|
-
} else if (urlElement.priority !== void 0) {
|
|
60
|
-
warnings.push({
|
|
61
|
-
type: "validation",
|
|
62
|
-
message: "Invalid priority value",
|
|
63
|
-
context: { url: urlElement.loc, field: "priority", value: urlElement.priority }
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
if (urlElement.image) {
|
|
67
|
-
const images = Array.isArray(urlElement.image) ? urlElement.image : [urlElement.image];
|
|
68
|
-
const validImages = images.map((img) => {
|
|
69
|
-
if (isValidString(img.loc)) {
|
|
70
|
-
return { loc: img.loc };
|
|
71
|
-
} else {
|
|
72
|
-
warnings.push({
|
|
73
|
-
type: "validation",
|
|
74
|
-
message: "Image missing required loc element",
|
|
75
|
-
context: { url: urlElement.loc, field: "image.loc" }
|
|
76
|
-
});
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}).filter((img) => img !== null);
|
|
80
|
-
if (validImages.length > 0) {
|
|
81
|
-
urlObj.images = validImages;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (urlElement.video) {
|
|
85
|
-
const videos = Array.isArray(urlElement.video) ? urlElement.video : [urlElement.video];
|
|
86
|
-
const validVideos = videos.map((video) => {
|
|
87
|
-
const missingFields = [];
|
|
88
|
-
if (!isValidString(video.title)) missingFields.push("title");
|
|
89
|
-
if (!isValidString(video.thumbnail_loc)) missingFields.push("thumbnail_loc");
|
|
90
|
-
if (!isValidString(video.description)) missingFields.push("description");
|
|
91
|
-
if (!isValidString(video.content_loc)) missingFields.push("content_loc");
|
|
92
|
-
if (missingFields.length > 0) {
|
|
93
|
-
warnings.push({
|
|
94
|
-
type: "validation",
|
|
95
|
-
message: `Video missing required fields: ${missingFields.join(", ")}`,
|
|
96
|
-
context: { url: urlElement.loc, field: "video" }
|
|
97
|
-
});
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
const videoObj = {
|
|
101
|
-
title: video.title,
|
|
102
|
-
thumbnail_loc: video.thumbnail_loc,
|
|
103
|
-
description: video.description,
|
|
104
|
-
content_loc: video.content_loc
|
|
105
|
-
};
|
|
106
|
-
if (isValidString(video.player_loc)) {
|
|
107
|
-
videoObj.player_loc = video.player_loc;
|
|
108
|
-
}
|
|
109
|
-
const duration = parseInteger(video.duration);
|
|
110
|
-
if (duration !== void 0) {
|
|
111
|
-
videoObj.duration = duration;
|
|
112
|
-
} else if (video.duration !== void 0) {
|
|
113
|
-
warnings.push({
|
|
114
|
-
type: "validation",
|
|
115
|
-
message: "Invalid video duration value",
|
|
116
|
-
context: { url: urlElement.loc, field: "video.duration", value: video.duration }
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
if (isValidString(video.expiration_date)) {
|
|
120
|
-
videoObj.expiration_date = video.expiration_date;
|
|
121
|
-
}
|
|
122
|
-
const rating = parseNumber(video.rating);
|
|
123
|
-
if (rating !== void 0) {
|
|
124
|
-
if (rating < 0 || rating > 5) {
|
|
125
|
-
warnings.push({
|
|
126
|
-
type: "validation",
|
|
127
|
-
message: "Video rating should be between 0.0 and 5.0",
|
|
128
|
-
context: { url: urlElement.loc, field: "video.rating", value: rating }
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
videoObj.rating = rating;
|
|
132
|
-
} else if (video.rating !== void 0) {
|
|
133
|
-
warnings.push({
|
|
134
|
-
type: "validation",
|
|
135
|
-
message: "Invalid video rating value",
|
|
136
|
-
context: { url: urlElement.loc, field: "video.rating", value: video.rating }
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
const viewCount = parseInteger(video.view_count);
|
|
140
|
-
if (viewCount !== void 0) {
|
|
141
|
-
videoObj.view_count = viewCount;
|
|
142
|
-
} else if (video.view_count !== void 0) {
|
|
143
|
-
warnings.push({
|
|
144
|
-
type: "validation",
|
|
145
|
-
message: "Invalid video view_count value",
|
|
146
|
-
context: { url: urlElement.loc, field: "video.view_count", value: video.view_count }
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
if (isValidString(video.publication_date)) {
|
|
150
|
-
videoObj.publication_date = video.publication_date;
|
|
151
|
-
}
|
|
152
|
-
if (isValidString(video.family_friendly)) {
|
|
153
|
-
const validValues = ["yes", "no"];
|
|
154
|
-
if (validValues.includes(video.family_friendly)) {
|
|
155
|
-
videoObj.family_friendly = video.family_friendly;
|
|
156
|
-
} else {
|
|
157
|
-
warnings.push({
|
|
158
|
-
type: "validation",
|
|
159
|
-
message: 'Invalid video family_friendly value, should be "yes" or "no"',
|
|
160
|
-
context: { url: urlElement.loc, field: "video.family_friendly", value: video.family_friendly }
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (isValidString(video.requires_subscription)) {
|
|
165
|
-
const validValues = ["yes", "no"];
|
|
166
|
-
if (validValues.includes(video.requires_subscription)) {
|
|
167
|
-
videoObj.requires_subscription = video.requires_subscription;
|
|
168
|
-
} else {
|
|
169
|
-
warnings.push({
|
|
170
|
-
type: "validation",
|
|
171
|
-
message: 'Invalid video requires_subscription value, should be "yes" or "no"',
|
|
172
|
-
context: { url: urlElement.loc, field: "video.requires_subscription", value: video.requires_subscription }
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (isValidString(video.live)) {
|
|
177
|
-
const validValues = ["yes", "no"];
|
|
178
|
-
if (validValues.includes(video.live)) {
|
|
179
|
-
videoObj.live = video.live;
|
|
180
|
-
} else {
|
|
181
|
-
warnings.push({
|
|
182
|
-
type: "validation",
|
|
183
|
-
message: 'Invalid video live value, should be "yes" or "no"',
|
|
184
|
-
context: { url: urlElement.loc, field: "video.live", value: video.live }
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (video.restriction && typeof video.restriction === "object") {
|
|
189
|
-
const restriction = video.restriction;
|
|
190
|
-
if (isValidString(restriction.relationship) && isValidString(restriction["#text"])) {
|
|
191
|
-
const validRelationships = ["allow", "deny"];
|
|
192
|
-
if (validRelationships.includes(restriction.relationship)) {
|
|
193
|
-
videoObj.restriction = {
|
|
194
|
-
relationship: restriction.relationship,
|
|
195
|
-
restriction: restriction["#text"]
|
|
196
|
-
};
|
|
197
|
-
} else {
|
|
198
|
-
warnings.push({
|
|
199
|
-
type: "validation",
|
|
200
|
-
message: 'Invalid video restriction relationship, should be "allow" or "deny"',
|
|
201
|
-
context: { url: urlElement.loc, field: "video.restriction.relationship", value: restriction.relationship }
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (video.platform && typeof video.platform === "object") {
|
|
207
|
-
const platform = video.platform;
|
|
208
|
-
if (isValidString(platform.relationship) && isValidString(platform["#text"])) {
|
|
209
|
-
const validRelationships = ["allow", "deny"];
|
|
210
|
-
if (validRelationships.includes(platform.relationship)) {
|
|
211
|
-
videoObj.platform = {
|
|
212
|
-
relationship: platform.relationship,
|
|
213
|
-
platform: platform["#text"]
|
|
214
|
-
};
|
|
215
|
-
} else {
|
|
216
|
-
warnings.push({
|
|
217
|
-
type: "validation",
|
|
218
|
-
message: 'Invalid video platform relationship, should be "allow" or "deny"',
|
|
219
|
-
context: { url: urlElement.loc, field: "video.platform.relationship", value: platform.relationship }
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
if (video.price) {
|
|
225
|
-
const prices = Array.isArray(video.price) ? video.price : [video.price];
|
|
226
|
-
const validPrices = prices.map((price) => {
|
|
227
|
-
const priceValue = price["#text"];
|
|
228
|
-
if (priceValue == null || typeof priceValue !== "string" && typeof priceValue !== "number") {
|
|
229
|
-
warnings.push({
|
|
230
|
-
type: "validation",
|
|
231
|
-
message: "Video price missing value",
|
|
232
|
-
context: { url: urlElement.loc, field: "video.price" }
|
|
233
|
-
});
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
const validTypes = ["rent", "purchase", "package", "subscription"];
|
|
237
|
-
if (price.type && !validTypes.includes(price.type)) {
|
|
238
|
-
warnings.push({
|
|
239
|
-
type: "validation",
|
|
240
|
-
message: `Invalid video price type "${price.type}", should be one of: ${validTypes.join(", ")}`,
|
|
241
|
-
context: { url: urlElement.loc, field: "video.price.type", value: price.type }
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
price: String(priceValue),
|
|
246
|
-
currency: price.currency,
|
|
247
|
-
type: price.type
|
|
248
|
-
};
|
|
249
|
-
}).filter((p) => p !== null);
|
|
250
|
-
if (validPrices.length > 0) {
|
|
251
|
-
videoObj.price = validPrices;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (video.uploader && typeof video.uploader === "object") {
|
|
255
|
-
const uploader = video.uploader;
|
|
256
|
-
if (isValidString(uploader.info) && isValidString(uploader["#text"])) {
|
|
257
|
-
videoObj.uploader = {
|
|
258
|
-
uploader: uploader["#text"],
|
|
259
|
-
info: uploader.info
|
|
260
|
-
};
|
|
261
|
-
} else {
|
|
262
|
-
warnings.push({
|
|
263
|
-
type: "validation",
|
|
264
|
-
message: "Video uploader missing required info or name",
|
|
265
|
-
context: { url: urlElement.loc, field: "video.uploader" }
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (video.tag) {
|
|
270
|
-
const tags = Array.isArray(video.tag) ? video.tag : [video.tag];
|
|
271
|
-
const validTags = tags.filter(isValidString);
|
|
272
|
-
if (validTags.length > 0) {
|
|
273
|
-
videoObj.tag = validTags;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return videoObj;
|
|
277
|
-
}).filter((video) => video !== null);
|
|
278
|
-
if (validVideos.length > 0) {
|
|
279
|
-
urlObj.videos = validVideos;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
if (urlElement.link) {
|
|
283
|
-
const links = Array.isArray(urlElement.link) ? urlElement.link : [urlElement.link];
|
|
284
|
-
const alternatives = links.map((link) => {
|
|
285
|
-
if (link.rel === "alternate" && isValidString(link.hreflang) && isValidString(link.href)) {
|
|
286
|
-
return {
|
|
287
|
-
hreflang: link.hreflang,
|
|
288
|
-
href: link.href
|
|
289
|
-
};
|
|
290
|
-
} else {
|
|
291
|
-
warnings.push({
|
|
292
|
-
type: "validation",
|
|
293
|
-
message: 'Alternative link missing required rel="alternate", hreflang, or href',
|
|
294
|
-
context: { url: urlElement.loc, field: "link" }
|
|
295
|
-
});
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
}).filter((alt) => alt !== null);
|
|
299
|
-
if (alternatives.length > 0) {
|
|
300
|
-
urlObj.alternatives = alternatives;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (urlElement.news && typeof urlElement.news === "object") {
|
|
304
|
-
const news = urlElement.news;
|
|
305
|
-
if (isValidString(news.title) && isValidString(news.publication_date) && news.publication && isValidString(news.publication.name) && isValidString(news.publication.language)) {
|
|
306
|
-
urlObj.news = {
|
|
307
|
-
title: news.title,
|
|
308
|
-
publication_date: news.publication_date,
|
|
309
|
-
publication: {
|
|
310
|
-
name: news.publication.name,
|
|
311
|
-
language: news.publication.language
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
} else {
|
|
315
|
-
warnings.push({
|
|
316
|
-
type: "validation",
|
|
317
|
-
message: "News entry missing required fields (title, publication_date, publication.name, publication.language)",
|
|
318
|
-
context: { url: urlElement.loc, field: "news" }
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
const filteredUrlObj = Object.fromEntries(
|
|
323
|
-
Object.entries(urlObj).filter(
|
|
324
|
-
([_, value]) => value != null && (!Array.isArray(value) || value.length > 0)
|
|
325
|
-
)
|
|
326
|
-
);
|
|
327
|
-
return filteredUrlObj;
|
|
328
|
-
}
|
|
329
|
-
async function parseSitemapXml(xml) {
|
|
330
|
-
const warnings = [];
|
|
331
|
-
if (!xml) {
|
|
332
|
-
throw new Error("Empty XML input provided");
|
|
333
|
-
}
|
|
334
|
-
const { XMLParser } = await import('fast-xml-parser');
|
|
335
|
-
const parser = new XMLParser({
|
|
336
|
-
isArray: (tagName) => ["url", "image", "video", "link", "tag", "price"].includes(tagName),
|
|
337
|
-
removeNSPrefix: true,
|
|
338
|
-
parseAttributeValue: false,
|
|
339
|
-
ignoreAttributes: false,
|
|
340
|
-
attributeNamePrefix: "",
|
|
341
|
-
trimValues: true
|
|
342
|
-
});
|
|
343
|
-
try {
|
|
344
|
-
const parsed = parser.parse(xml);
|
|
345
|
-
if (!parsed?.urlset) {
|
|
346
|
-
throw new Error("XML does not contain a valid urlset element");
|
|
347
|
-
}
|
|
348
|
-
if (!parsed.urlset.url) {
|
|
349
|
-
throw new Error("Sitemap contains no URL entries");
|
|
350
|
-
}
|
|
351
|
-
const urls = Array.isArray(parsed.urlset.url) ? parsed.urlset.url : [parsed.urlset.url];
|
|
352
|
-
const validUrls = urls.map((url) => extractUrlFromParsedElement(url, warnings)).filter((url) => url !== null);
|
|
353
|
-
if (validUrls.length === 0 && urls.length > 0) {
|
|
354
|
-
warnings.push({
|
|
355
|
-
type: "validation",
|
|
356
|
-
message: "No valid URLs found in sitemap after validation"
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
return { urls: validUrls, warnings };
|
|
360
|
-
} catch (error) {
|
|
361
|
-
if (error instanceof Error && (error.message === "Empty XML input provided" || error.message === "XML does not contain a valid urlset element" || error.message === "Sitemap contains no URL entries")) {
|
|
362
|
-
throw error;
|
|
363
|
-
}
|
|
364
|
-
throw new Error(`Failed to parse XML: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
export { parseSitemapXml };
|