next-advanced-sitemap 1.1.2 → 1.1.3
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/README.md +141 -35
- package/dist/index.cjs +14 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,23 +11,22 @@ While Next.js provides a built-in `MetadataRoute.Sitemap` utility, it currently
|
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
|
-
- **Google Images Support**:
|
|
15
|
-
- **Image Accessibility
|
|
16
|
-
- **Google Video Support**:
|
|
17
|
-
- **Video Engagement Metrics (v1.1.3
|
|
18
|
-
- **Google Video Live Streaming (v1.1.1)**: Native injection of the `<video:live>` parameter to flag real-time broadcasts and instantly trigger red **LIVE** badges on Google SERP matrices.
|
|
19
|
-
- **Google News Support**:
|
|
20
|
-
- **Internationalization**: Seamless
|
|
21
|
-
- **Priority Auto-Sorting (v1.0.8)**: Optional deterministic descending sort (`1.0` to `0.0`) based on entry weights to present your most strategic pages to crawlers first.
|
|
22
|
-
- **Auto-Trimming Sanitization (v1.0.7)**: Automatic `.trim()` execution on all URL
|
|
23
|
-
- **Native Date Polymorphism (v1.0.6)**: Full support for native JavaScript `Date` objects inside
|
|
24
|
-
- **Strict SEO Enum Typing (v1.0.5)**: Compile-time validation and IDE autocompletion for `changefreq` and `priority`
|
|
25
|
-
- **Strict Structural Validation (v1.0.4)**: Advanced URL parsing using the platform-native engine to intercept syntax errors and unencoded
|
|
26
|
-
- **Auto-lastmod (v1.0.3)**: Optional automatic injection of the current system date for entries missing
|
|
27
|
-
- **
|
|
28
|
-
- **Developer Experience**: Fully typed with TypeScript, zero external dependencies, and optimized for Next.js Route Handlers.
|
|
14
|
+
- **Google Images Support**: Complete indexation of visual assets with support for titles, captions, local SEO positioning, and copyright protections.
|
|
15
|
+
- **Image Accessibility Protection (v1.1.2)**: Advanced preventive protection against empty text strings or spaces (`.trim()`) in `title` and `caption` fields to completely eliminate malformed empty XML tokens.
|
|
16
|
+
- **Google Video Support**: Boost video search layouts and video-carousel presence on Google Search with complete structured data encapsulation.
|
|
17
|
+
- **Video Engagement Metrics & Validation (v1.1.3)**: Native integration of `<video:duration>` and `<video:view_count>` statistical metrics featuring deterministic float truncation (`Math.floor`) and strict bounding boundaries (0 to 28,800 seconds max).
|
|
18
|
+
- **Google Video Live Streaming (v1.1.1)**: Native injection of the `<video:live>` parameter to flag active real-time broadcasts and instantly trigger red **LIVE** badges on Google SERP matrices.
|
|
19
|
+
- **Google News Support**: Instant discovery for news publications with strict support for required news name, language tag, and publication date attributes.
|
|
20
|
+
- **Internationalization (Hreflang)**: Seamless rendering of `xhtml:link` relation tags to govern multi-regional and multilingual indexing across global markets.
|
|
21
|
+
- **Priority Auto-Sorting (v1.0.8)**: Optional deterministic descending sort (`1.0` down to `0.0`) based on entry weights to present your most strategic revenue-driving pages to crawlers first.
|
|
22
|
+
- **Auto-Trimming Sanitization (v1.0.7)**: Automatic `.trim()` execution on all URL structures to silently correct leading/trailing whitespace errors originating from CMS fields or raw databases.
|
|
23
|
+
- **Native Date Polymorphism (v1.0.6)**: Full support for native JavaScript `Date` objects inside all extensions—handling internal conversion and structural formatting automatically.
|
|
24
|
+
- **Strict SEO Enum Typing (v1.0.5)**: Compile-time validation and IDE autocompletion for `changefreq` and `priority` keys to completely lock out manual layout typos.
|
|
25
|
+
- **Strict Structural Validation (v1.0.4)**: Advanced URL parsing using the platform-native engine to intercept syntax errors and unencoded internal spaces before application deployment.
|
|
26
|
+
- **Auto-lastmod (v1.0.3)**: Optional automatic injection of the current system ISO date for entries missing an explicit `lastmod` tracking value.
|
|
27
|
+
- **Deep XML Metadata Escaping (v1.0.2)**: Enhanced, high-performance regex processor to safely handle complex special characters (`&`, `"`, `'`, `<`, `>`) inside titles, descriptions, and captions.
|
|
29
28
|
- **Custom TTL Cache-Control (v1.0.9)**: Direct control over sitemap caching persistence using a clean `maxAge` configuration option to lower crawl footprints on backend nodes.
|
|
30
|
-
- **Local SEO & Image Licensing (v1.1.0)**:
|
|
29
|
+
- **Local SEO & Image Licensing (v1.1.0)**: Support for `geo_location` parameters and programmatic `license` badges to trigger Google's image search retail overlay.
|
|
31
30
|
|
|
32
31
|
## Installation
|
|
33
32
|
|
|
@@ -59,10 +58,12 @@ export async function GET() {
|
|
|
59
58
|
priority: 0.9,
|
|
60
59
|
videos: [
|
|
61
60
|
{
|
|
62
|
-
thumbnail_loc: 'https://fomadev.com/thumbs/live.jpg',
|
|
61
|
+
thumbnail_loc: 'https://fomadev.com/thumbs/live.jpg ',
|
|
63
62
|
title: 'FomaDev Live Tech Session',
|
|
64
63
|
description: 'Building production-grade packages with Next.js.',
|
|
65
64
|
publication_date: new Date(),
|
|
65
|
+
duration: 3600, // v1.1.3: Statistical metric (Duration in seconds)
|
|
66
|
+
view_count: 1420, // v1.1.3: Engagement metric (Views integer)
|
|
66
67
|
live: 'yes' // v1.1.1: Triggers the official Google LIVE badge on SERP
|
|
67
68
|
}
|
|
68
69
|
]
|
|
@@ -117,59 +118,164 @@ Generates a standard Next.js `Response` object with the correct `application/xml
|
|
|
117
118
|
<tbody>
|
|
118
119
|
<tr>
|
|
119
120
|
<td><code>url</code></td>
|
|
120
|
-
<td
|
|
121
|
-
<td>
|
|
121
|
+
<td>string</td>
|
|
122
|
+
<td><strong>Required.</strong> Absolute target link (must begin with http:// or https://).</td>
|
|
122
123
|
</tr>
|
|
123
124
|
<tr>
|
|
124
125
|
<td><code>lastmod</code></td>
|
|
125
|
-
<td
|
|
126
|
-
<td>
|
|
126
|
+
<td>Date | string</td>
|
|
127
|
+
<td>Optional tracking timestamp reflecting last structural update.</td>
|
|
127
128
|
</tr>
|
|
128
129
|
<tr>
|
|
129
130
|
<td><code>changefreq</code></td>
|
|
130
|
-
<td
|
|
131
|
-
<td>
|
|
131
|
+
<td>SitemapChangeFreq</td>
|
|
132
|
+
<td>Optional hint keyword mapped to engine crawling loops</td>
|
|
132
133
|
</tr>
|
|
133
134
|
<tr>
|
|
134
135
|
<td><code>priority</code></td>
|
|
135
|
-
<td
|
|
136
|
-
<td>
|
|
136
|
+
<td>SitemapPriority</td>
|
|
137
|
+
<td>Optional weight coefficient bounding page value from 0.0 to 1.0.</td>
|
|
137
138
|
</tr>
|
|
138
139
|
<tr>
|
|
139
140
|
<td><code>images</code></td>
|
|
140
|
-
<td
|
|
141
|
-
<td>
|
|
141
|
+
<td>SitemapImage[]</td>
|
|
142
|
+
<td>Optional array containing structural metadata assets for Google Images.</td>
|
|
142
143
|
</tr>
|
|
143
144
|
<tr>
|
|
144
145
|
<td><code>videos</code></td>
|
|
145
|
-
<td
|
|
146
|
-
<td>
|
|
146
|
+
<td>SitemapVideo[]</td>
|
|
147
|
+
<td>Optional array conveying detailed schemas for rich video indexation.</td>
|
|
147
148
|
</tr>
|
|
148
149
|
<tr>
|
|
149
150
|
<td><code>news</code></td>
|
|
150
|
-
<td
|
|
151
|
-
<td>
|
|
151
|
+
<td>SitemapNews</td>
|
|
152
|
+
<td>Optional integration configuration complying with Google News indexing rules.</td>
|
|
152
153
|
</tr>
|
|
153
154
|
<tr>
|
|
154
155
|
<td><code>alternates</code></td>
|
|
155
|
-
<td
|
|
156
|
-
<td>
|
|
156
|
+
<td>SitemapAlternate[]</td>
|
|
157
|
+
<td>Optional translation links array serving Hreflang indexing loops.</td>
|
|
157
158
|
</tr>
|
|
158
159
|
<tr>
|
|
159
160
|
<td><code>geo_location</code></td>
|
|
160
|
-
<td
|
|
161
|
+
<td>string</td>
|
|
161
162
|
<td>(Optional) Geographic location string of the image (e.g., "Kinshasa, DRC").</td>
|
|
162
163
|
</tr>
|
|
163
164
|
<tr>
|
|
164
165
|
<td><code>license</code></td>
|
|
165
|
-
<td
|
|
166
|
+
<td>string</td>
|
|
166
167
|
<td>(Optional) Valid HTTP/HTTPS URL addressing the licensing rights or usage terms of the image asset.</td>
|
|
167
168
|
</tr>
|
|
168
169
|
</tbody>
|
|
169
170
|
</table>
|
|
170
171
|
|
|
172
|
+
### SitemapImage
|
|
173
|
+
|
|
174
|
+
<table>
|
|
175
|
+
<thead>
|
|
176
|
+
<tr>
|
|
177
|
+
<th>Property</th>
|
|
178
|
+
<th>Type</th>
|
|
179
|
+
<th>Description</th>
|
|
180
|
+
</tr>
|
|
181
|
+
</thead>
|
|
182
|
+
<tbody>
|
|
183
|
+
<tr>
|
|
184
|
+
<td><code>loc</code></td>
|
|
185
|
+
<td>string</td>
|
|
186
|
+
<td><strong>Required.</strong> The absolute URL targeting the source image asset.</td>
|
|
187
|
+
</tr>
|
|
188
|
+
<tr>
|
|
189
|
+
<td><code>title</code></td>
|
|
190
|
+
<td>string</td>
|
|
191
|
+
<td>Optional text representation describing the visual asset. Auto-trimmed.</td>
|
|
192
|
+
</tr>
|
|
193
|
+
<tr>
|
|
194
|
+
<td><code>caption</code></td>
|
|
195
|
+
<td>string</td>
|
|
196
|
+
<td>Optional descriptive context surrounding the element. Deep XML Escaped.</td>
|
|
197
|
+
</tr>
|
|
198
|
+
<tr>
|
|
199
|
+
<td><code>geo_location</code></td>
|
|
200
|
+
<td>string</td>
|
|
201
|
+
<td>Optional location reference (e.g., "Kinshasa, Democratic Republic of the Congo").</td>
|
|
202
|
+
</tr>
|
|
203
|
+
<tr>
|
|
204
|
+
<td><code>license</code></td>
|
|
205
|
+
<td>string</td>
|
|
206
|
+
<td>Optional absolute URL containing intellectual copyright terms or usage badges.</td>
|
|
207
|
+
</tr>
|
|
208
|
+
</tbody>
|
|
209
|
+
</table>
|
|
210
|
+
|
|
211
|
+
### SitemapVideo
|
|
212
|
+
|
|
213
|
+
<table>
|
|
214
|
+
<thead>
|
|
215
|
+
<tr>
|
|
216
|
+
<th>Property</th>
|
|
217
|
+
<th>Type</th>
|
|
218
|
+
<th>Description</th>
|
|
219
|
+
</tr>
|
|
220
|
+
</thead>
|
|
221
|
+
<tbody>
|
|
222
|
+
<tr>
|
|
223
|
+
<td><code>thumbnail_loc</code></td>
|
|
224
|
+
<td>string</td>
|
|
225
|
+
<td><strong>Required.</strong> The absolute URL targeting the source image asset.</td>
|
|
226
|
+
</tr>
|
|
227
|
+
<tr>
|
|
228
|
+
<td><code>title</code></td>
|
|
229
|
+
<td>string</td>
|
|
230
|
+
<td><strong>Required.</strong> The descriptive headline of the video asset. Escaped.</td>
|
|
231
|
+
</tr>
|
|
232
|
+
<tr>
|
|
233
|
+
<td><code>description</code></td>
|
|
234
|
+
<td>string</td>
|
|
235
|
+
<td><strong>Required.</strong> Summary text representing the video topic. Max 2048 chars.</td>
|
|
236
|
+
</tr>
|
|
237
|
+
<tr>
|
|
238
|
+
<td><code>publication_date</code></td>
|
|
239
|
+
<td>Date | string</td>
|
|
240
|
+
<td><strong>Required.</strong> Publication date object or raw formatted ISO string.</td>
|
|
241
|
+
</tr>
|
|
242
|
+
<tr>
|
|
243
|
+
<td><code>content_loc</code></td>
|
|
244
|
+
<td>string</td>
|
|
245
|
+
<td>Optional absolute URL targeting the raw video media stream container.</td>
|
|
246
|
+
</tr>
|
|
247
|
+
<tr>
|
|
248
|
+
<td><code>player_loc</code></td>
|
|
249
|
+
<td>string</td>
|
|
250
|
+
<td>Optional absolute URL linking out to an embeddable video player frame.</td>
|
|
251
|
+
</tr>
|
|
252
|
+
<tr>
|
|
253
|
+
<td><code>duration</code></td>
|
|
254
|
+
<td>number</td>
|
|
255
|
+
<td>Optional length in seconds. Must be an integer bounded between 0 and 28800.</td>
|
|
256
|
+
</tr>
|
|
257
|
+
<tr>
|
|
258
|
+
<td><code>view_count</code></td>
|
|
259
|
+
<td>number</td>
|
|
260
|
+
<td>Optional overall hit counter. Negative values strictly prohibited.</td>
|
|
261
|
+
</tr>
|
|
262
|
+
<tr>
|
|
263
|
+
<td><code>live</code></td>
|
|
264
|
+
<td>'yes' | 'no'</td>
|
|
265
|
+
<td>Optional switch triggering immediate Google SERP LIVE badges.</td>
|
|
266
|
+
</tr>
|
|
267
|
+
</tbody>
|
|
268
|
+
</table>
|
|
269
|
+
|
|
171
270
|
## Technical Implementation
|
|
172
271
|
|
|
272
|
+
### Strict Video Statistical Enforcement (v1.1.3)
|
|
273
|
+
Google's ingestion schema specifies rigid rules for video engagement parameters. Providing decimals or numbers outside structural limits can invalidate the entire sitemap file inside the Google Search Console.
|
|
274
|
+
|
|
275
|
+
- **Range Locking**: The generator enforces that any provided `duration` fits within a strict `0` to `28,800` seconds bracket (up to 8 hours). Breaking this threshold or inputting negative values immediately throws an descriptive runtime exception.
|
|
276
|
+
|
|
277
|
+
- **Decimal Truncation**: Both `duration` and `view_count` properties undergo automated conversion into integers using deterministic mathematical grounding (`Math.floor`). This allows systems to relay float-heavy numbers straight from analytics stores safely.
|
|
278
|
+
|
|
173
279
|
### Image Accessibility & E-commerce Protection
|
|
174
280
|
Large e-commerce platform backends or multi-vendor platforms frequently inject messy string data from user-generated fields—such as alternative text filled with raw spaces (`" "`) or unescaped description metadata containing special HTML entities (`&`, `<`, `>`). To achieve strict alignment with Googlebot accessibility schemas without risking layout parsing crashes, the engine implements a two-tier architectural protective filter in **v1.1.2**:
|
|
175
281
|
|
package/dist/index.cjs
CHANGED
|
@@ -179,11 +179,23 @@ function generateXml(entries, options = {}) {
|
|
|
179
179
|
`;
|
|
180
180
|
}
|
|
181
181
|
if (vid.duration !== void 0) {
|
|
182
|
-
|
|
182
|
+
const finalDuration = Math.floor(vid.duration);
|
|
183
|
+
if (finalDuration < 0 || finalDuration > 28800) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`[next-advanced-sitemap] Invalid video duration: ${finalDuration}. Duration must be an integer between 0 and 28800 seconds (8 hours).`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
xml += ` <video:duration>${finalDuration}</video:duration>
|
|
183
189
|
`;
|
|
184
190
|
}
|
|
185
191
|
if (vid.view_count !== void 0) {
|
|
186
|
-
|
|
192
|
+
const finalViewCount = Math.floor(vid.view_count);
|
|
193
|
+
if (finalViewCount < 0) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`[next-advanced-sitemap] Invalid video view_count: ${finalViewCount}. View count cannot be negative.`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
xml += ` <video:view_count>${finalViewCount}</video:view_count>
|
|
187
199
|
`;
|
|
188
200
|
}
|
|
189
201
|
if (vid.live) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/utils/xml-escape.ts","../src/core/generator.ts"],"sourcesContent":["/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from './types/sitemap.js';\nimport { generateXml } from './core/generator.js';\n\nexport * from './types/sitemap.js';\n\n/**\n * Génère une réponse HTTP compatible Next.js (App Router) avec options de configuration.\n * v1.0.9 : Injection dynamique et personnalisable de l'en-tête Cache-Control via l'option maxAge\n * * @param entries - Liste des entrées du sitemap\n * @param options - Options de génération et de mise en cache (ex: autoLastmod, maxAge)\n * @returns Une instance de Response contenant le flux XML configuré\n */\nexport function getServerSitemapResponse(\n entries: SitemapEntry[], \n options: SitemapOptions = {}\n): Response {\n const xml = generateXml(entries, options);\n\n // Détermination de la stratégie de mise en cache (v1.0.9)\n const cacheControlHeader = options.maxAge !== undefined\n ? `public, max-age=${options.maxAge}, must-revalidate`\n : 'public, s-maxage=86400, stale-while-revalidate';\n\n return new Response(xml, {\n headers: {\n 'Content-Type': 'application/xml',\n 'Cache-Control': cacheControlHeader,\n },\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\n/**\n * Convertit les caractères spéciaux en entités XML pour éviter la corruption du fichier.\n * Gère : <, >, &, \", '\n */\nexport function escapeXml(unsafe: string | undefined | null): string {\n if (!unsafe) return '';\n \n return unsafe.replace(/[<>&\"']/g, (c) => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\"': return '"';\n case \"'\": return ''';\n default: return c;\n }\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from '../types/sitemap.js';\nimport { escapeXml } from '../utils/xml-escape.js';\n\n/**\n * Nettoie et valide de manière stricte le format et la structure d'une URL.\n * v1.0.7 : Intégration de l'Auto-Trimming (nettoyage des espaces de début et de fin)\n */\nfunction sanitizeAndValidateUrl(rawUrl: string, context: string): string {\n const url = rawUrl ? rawUrl.trim() : '';\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n throw new Error(\n `[next-advanced-sitemap] Invalid URL in ${context}: \"${url}\". URLs must start with http:// or https://`\n );\n }\n\n if (url.includes(' ')) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n let isValid = false;\n if (typeof URL.canParse === 'function') {\n isValid = URL.canParse(url);\n } else {\n try {\n new URL(url);\n isValid = true;\n } catch {\n isValid = false;\n }\n }\n\n if (!isValid) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n return url;\n}\n\n/**\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\n * v1.1.2 : Métadonnées d'Accessibilité Images (caption & title optionnels, renforcés et nettoyés)\n * + Support sécurisé des statistiques vidéo (duration & view_count)\n */\nexport function generateXml(entries: SitemapEntry[], options: SitemapOptions = {}): string {\n const now = new Date().toISOString();\n let finalEntries = [...entries];\n\n if (options.sortByPriority) {\n finalEntries.sort((a, b) => {\n const priorityA = a.priority !== undefined ? (a.priority as number) : 0.5;\n const priorityB = b.priority !== undefined ? (b.priority as number) : 0.5;\n return priorityB - priorityA;\n });\n }\n \n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\n\n for (const entry of finalEntries) {\n const cleanMainUrl = sanitizeAndValidateUrl(entry.url, 'main entry');\n\n xml += ` <url>\\n`;\n xml += ` <loc>${escapeXml(cleanMainUrl)}</loc>\\n`;\n\n if (entry.alternates?.length) {\n for (const alt of entry.alternates) {\n const cleanAltUrl = sanitizeAndValidateUrl(alt.href, 'alternate link');\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(cleanAltUrl)}\" />\\n`;\n }\n }\n\n let lastmodValue = entry.lastmod;\n if (options.autoLastmod && !lastmodValue) {\n lastmodValue = now;\n }\n\n if (lastmodValue) {\n const date = lastmodValue instanceof Date ? lastmodValue.toISOString() : lastmodValue;\n xml += ` <lastmod>${date}</lastmod>\\n`;\n }\n\n if (entry.changefreq) {\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\n }\n\n if (entry.priority !== undefined) {\n xml += ` <priority>${(entry.priority as number).toFixed(1)}</priority>\\n`;\n }\n\n // Extension Images - v1.1.2 renforcée\n if (entry.images?.length) {\n for (const img of entry.images) {\n const cleanImgUrl = sanitizeAndValidateUrl(img.loc, 'image location');\n \n xml += ` <image:image>\\n`;\n xml += ` <image:loc>${escapeXml(cleanImgUrl)}</image:loc>\\n`;\n \n // ✨ v1.1.2 : .trim() préventif pour s'assurer qu'un texte blanc ne crée pas de balise invalide\n if (img.title && img.title.trim() !== '') {\n xml += ` <image:title>${escapeXml(img.title.trim())}</image:title>\\n`;\n }\n if (img.caption && img.caption.trim() !== '') {\n xml += ` <image:caption>${escapeXml(img.caption.trim())}</image:caption>\\n`;\n }\n \n // SEO Local\n if (img.geo_location && img.geo_location.trim() !== '') {\n xml += ` <image:geo_location>${escapeXml(img.geo_location.trim())}</image:geo_location>\\n`;\n }\n \n // Gestion des Licences Google Images\n if (img.license) {\n const cleanLicenseUrl = sanitizeAndValidateUrl(img.license, 'image license URL');\n xml += ` <image:license>${escapeXml(cleanLicenseUrl)}</image:license>\\n`;\n }\n \n xml += ` </image:image>\\n`;\n }\n }\n\n // Extension Vidéos\n if (entry.videos?.length) {\n for (const vid of entry.videos) {\n const cleanThumbLoc = sanitizeAndValidateUrl(vid.thumbnail_loc, 'video thumbnail');\n const cleanContentLoc = vid.content_loc ? sanitizeAndValidateUrl(vid.content_loc, 'video content location') : undefined;\n const cleanPlayerLoc = vid.player_loc ? sanitizeAndValidateUrl(vid.player_loc, 'video player location') : undefined;\n\n xml += ` <video:video>\\n`;\n xml += ` <video:thumbnail_loc>${escapeXml(cleanThumbLoc)}</video:thumbnail_loc>\\n`;\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\n \n if (cleanContentLoc) xml += ` <video:content_loc>${escapeXml(cleanContentLoc)}</video:content_loc>\\n`;\n if (cleanPlayerLoc) xml += ` <video:player_loc>${escapeXml(cleanPlayerLoc)}</video:player_loc>\\n`;\n \n if (vid.publication_date) {\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\n }\n\n // Métadonnées de statistiques (v1.1.3 - Préparé et sécurisé)\n if (vid.duration !== undefined) {\n xml += ` <video:duration>${Math.floor(vid.duration)}</video:duration>\\n`;\n }\n if (vid.view_count !== undefined) {\n xml += ` <video:view_count>${Math.floor(vid.view_count)}</video:view_count>\\n`;\n }\n\n // Support du Live Streaming v1.1.1\n if (vid.live) {\n xml += ` <video:live>${vid.live}</video:live>\\n`;\n }\n\n xml += ` </video:video>\\n`;\n }\n }\n\n // Extension News\n if (entry.news) {\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\n xml += ` <news:news>\\n`;\n xml += ` <news:publication>\\n`;\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\n xml += ` </news:publication>\\n`;\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\n xml += ` </news:news>\\n`;\n }\n\n xml += ` </url>\\n`;\n }\n\n xml += `</urlset>`;\n return xml;\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/xml-escape.ts","../src/core/generator.ts"],"sourcesContent":["/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from './types/sitemap.js';\nimport { generateXml } from './core/generator.js';\n\nexport * from './types/sitemap.js';\n\n/**\n * Génère une réponse HTTP compatible Next.js (App Router) avec options de configuration.\n * v1.0.9 : Injection dynamique et personnalisable de l'en-tête Cache-Control via l'option maxAge\n * * @param entries - Liste des entrées du sitemap\n * @param options - Options de génération et de mise en cache (ex: autoLastmod, maxAge)\n * @returns Une instance de Response contenant le flux XML configuré\n */\nexport function getServerSitemapResponse(\n entries: SitemapEntry[], \n options: SitemapOptions = {}\n): Response {\n const xml = generateXml(entries, options);\n\n // Détermination de la stratégie de mise en cache (v1.0.9)\n const cacheControlHeader = options.maxAge !== undefined\n ? `public, max-age=${options.maxAge}, must-revalidate`\n : 'public, s-maxage=86400, stale-while-revalidate';\n\n return new Response(xml, {\n headers: {\n 'Content-Type': 'application/xml',\n 'Cache-Control': cacheControlHeader,\n },\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\n/**\n * Convertit les caractères spéciaux en entités XML pour éviter la corruption du fichier.\n * Gère : <, >, &, \", '\n */\nexport function escapeXml(unsafe: string | undefined | null): string {\n if (!unsafe) return '';\n \n return unsafe.replace(/[<>&\"']/g, (c) => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\"': return '"';\n case \"'\": return ''';\n default: return c;\n }\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from '../types/sitemap.js';\nimport { escapeXml } from '../utils/xml-escape.js';\n\n/**\n * Nettoie et valide de manière stricte le format et la structure d'une URL.\n * v1.0.7 : Intégration de l'Auto-Trimming (nettoyage des espaces de début et de fin)\n */\nfunction sanitizeAndValidateUrl(rawUrl: string, context: string): string {\n const url = rawUrl ? rawUrl.trim() : '';\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n throw new Error(\n `[next-advanced-sitemap] Invalid URL in ${context}: \"${url}\". URLs must start with http:// or https://`\n );\n }\n\n if (url.includes(' ')) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n let isValid = false;\n if (typeof URL.canParse === 'function') {\n isValid = URL.canParse(url);\n } else {\n try {\n new URL(url);\n isValid = true;\n } catch {\n isValid = false;\n }\n }\n\n if (!isValid) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n return url;\n}\n\n/**\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\n * v1.1.3 : Validation stricte des statistiques vidéo (duration entre 0-28800s & view_count >= 0)\n */\nexport function generateXml(entries: SitemapEntry[], options: SitemapOptions = {}): string {\n const now = new Date().toISOString();\n let finalEntries = [...entries];\n\n if (options.sortByPriority) {\n finalEntries.sort((a, b) => {\n const priorityA = a.priority !== undefined ? (a.priority as number) : 0.5;\n const priorityB = b.priority !== undefined ? (b.priority as number) : 0.5;\n return priorityB - priorityA;\n });\n }\n \n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\n\n for (const entry of finalEntries) {\n const cleanMainUrl = sanitizeAndValidateUrl(entry.url, 'main entry');\n\n xml += ` <url>\\n`;\n xml += ` <loc>${escapeXml(cleanMainUrl)}</loc>\\n`;\n\n if (entry.alternates?.length) {\n for (const alt of entry.alternates) {\n const cleanAltUrl = sanitizeAndValidateUrl(alt.href, 'alternate link');\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(cleanAltUrl)}\" />\\n`;\n }\n }\n\n let lastmodValue = entry.lastmod;\n if (options.autoLastmod && !lastmodValue) {\n lastmodValue = now;\n }\n\n if (lastmodValue) {\n const date = lastmodValue instanceof Date ? lastmodValue.toISOString() : lastmodValue;\n xml += ` <lastmod>${date}</lastmod>\\n`;\n }\n\n if (entry.changefreq) {\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\n }\n\n if (entry.priority !== undefined) {\n xml += ` <priority>${(entry.priority as number).toFixed(1)}</priority>\\n`;\n }\n\n // Extension Images\n if (entry.images?.length) {\n for (const img of entry.images) {\n const cleanImgUrl = sanitizeAndValidateUrl(img.loc, 'image location');\n \n xml += ` <image:image>\\n`;\n xml += ` <image:loc>${escapeXml(cleanImgUrl)}</image:loc>\\n`;\n \n if (img.title && img.title.trim() !== '') {\n xml += ` <image:title>${escapeXml(img.title.trim())}</image:title>\\n`;\n }\n if (img.caption && img.caption.trim() !== '') {\n xml += ` <image:caption>${escapeXml(img.caption.trim())}</image:caption>\\n`;\n }\n \n if (img.geo_location && img.geo_location.trim() !== '') {\n xml += ` <image:geo_location>${escapeXml(img.geo_location.trim())}</image:geo_location>\\n`;\n }\n \n if (img.license) {\n const cleanLicenseUrl = sanitizeAndValidateUrl(img.license, 'image license URL');\n xml += ` <image:license>${escapeXml(cleanLicenseUrl)}</image:license>\\n`;\n }\n \n xml += ` </image:image>\\n`;\n }\n }\n\n // Extension Vidéos - v1.1.3 sécurisée\n if (entry.videos?.length) {\n for (const vid of entry.videos) {\n const cleanThumbLoc = sanitizeAndValidateUrl(vid.thumbnail_loc, 'video thumbnail');\n const cleanContentLoc = vid.content_loc ? sanitizeAndValidateUrl(vid.content_loc, 'video content location') : undefined;\n const cleanPlayerLoc = vid.player_loc ? sanitizeAndValidateUrl(vid.player_loc, 'video player location') : undefined;\n\n xml += ` <video:video>\\n`;\n xml += ` <video:thumbnail_loc>${escapeXml(cleanThumbLoc)}</video:thumbnail_loc>\\n`;\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\n \n if (cleanContentLoc) xml += ` <video:content_loc>${escapeXml(cleanContentLoc)}</video:content_loc>\\n`;\n if (cleanPlayerLoc) xml += ` <video:player_loc>${escapeXml(cleanPlayerLoc)}</video:player_loc>\\n`;\n \n if (vid.publication_date) {\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\n }\n\n // ✨ Validation et Sérialisation de la durée (0 - 28800s)\n if (vid.duration !== undefined) {\n const finalDuration = Math.floor(vid.duration);\n if (finalDuration < 0 || finalDuration > 28800) {\n throw new Error(\n `[next-advanced-sitemap] Invalid video duration: ${finalDuration}. Duration must be an integer between 0 and 28800 seconds (8 hours).`\n );\n }\n xml += ` <video:duration>${finalDuration}</video:duration>\\n`;\n }\n\n // ✨ Validation et Sérialisation du nombre de vues (>= 0)\n if (vid.view_count !== undefined) {\n const finalViewCount = Math.floor(vid.view_count);\n if (finalViewCount < 0) {\n throw new Error(\n `[next-advanced-sitemap] Invalid video view_count: ${finalViewCount}. View count cannot be negative.`\n );\n }\n xml += ` <video:view_count>${finalViewCount}</video:view_count>\\n`;\n }\n\n if (vid.live) {\n xml += ` <video:live>${vid.live}</video:live>\\n`;\n }\n\n xml += ` </video:video>\\n`;\n }\n }\n\n // Extension News\n if (entry.news) {\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\n xml += ` <news:news>\\n`;\n xml += ` <news:publication>\\n`;\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\n xml += ` </news:publication>\\n`;\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\n xml += ` </news:news>\\n`;\n }\n\n xml += ` </url>\\n`;\n }\n\n xml += `</urlset>`;\n return xml;\n}"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -39,12 +39,19 @@ interface SitemapVideo {
|
|
|
39
39
|
description: string;
|
|
40
40
|
content_loc?: string;
|
|
41
41
|
player_loc?: string;
|
|
42
|
-
duration?: number;
|
|
43
|
-
view_count?: number;
|
|
44
42
|
publication_date?: Date | string;
|
|
45
43
|
family_friendly?: 'yes' | 'no';
|
|
46
44
|
/** (Optional) v1.1.1: Indique si la vidéo est une diffusion en direct ('yes' ou 'no'). */
|
|
47
45
|
live?: 'yes' | 'no';
|
|
46
|
+
/**
|
|
47
|
+
* (Optional) La durée de la vidéo en secondes.
|
|
48
|
+
* Doit être un entier compris entre 0 et 28800 (8 heures).
|
|
49
|
+
*/
|
|
50
|
+
duration?: number;
|
|
51
|
+
/**
|
|
52
|
+
* (Optional) Le nombre de vues de la vidéo.
|
|
53
|
+
*/
|
|
54
|
+
view_count?: number;
|
|
48
55
|
}
|
|
49
56
|
/**
|
|
50
57
|
* Interface pour Google News
|
package/dist/index.d.ts
CHANGED
|
@@ -39,12 +39,19 @@ interface SitemapVideo {
|
|
|
39
39
|
description: string;
|
|
40
40
|
content_loc?: string;
|
|
41
41
|
player_loc?: string;
|
|
42
|
-
duration?: number;
|
|
43
|
-
view_count?: number;
|
|
44
42
|
publication_date?: Date | string;
|
|
45
43
|
family_friendly?: 'yes' | 'no';
|
|
46
44
|
/** (Optional) v1.1.1: Indique si la vidéo est une diffusion en direct ('yes' ou 'no'). */
|
|
47
45
|
live?: 'yes' | 'no';
|
|
46
|
+
/**
|
|
47
|
+
* (Optional) La durée de la vidéo en secondes.
|
|
48
|
+
* Doit être un entier compris entre 0 et 28800 (8 heures).
|
|
49
|
+
*/
|
|
50
|
+
duration?: number;
|
|
51
|
+
/**
|
|
52
|
+
* (Optional) Le nombre de vues de la vidéo.
|
|
53
|
+
*/
|
|
54
|
+
view_count?: number;
|
|
48
55
|
}
|
|
49
56
|
/**
|
|
50
57
|
* Interface pour Google News
|
package/dist/index.js
CHANGED
|
@@ -153,11 +153,23 @@ function generateXml(entries, options = {}) {
|
|
|
153
153
|
`;
|
|
154
154
|
}
|
|
155
155
|
if (vid.duration !== void 0) {
|
|
156
|
-
|
|
156
|
+
const finalDuration = Math.floor(vid.duration);
|
|
157
|
+
if (finalDuration < 0 || finalDuration > 28800) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`[next-advanced-sitemap] Invalid video duration: ${finalDuration}. Duration must be an integer between 0 and 28800 seconds (8 hours).`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
xml += ` <video:duration>${finalDuration}</video:duration>
|
|
157
163
|
`;
|
|
158
164
|
}
|
|
159
165
|
if (vid.view_count !== void 0) {
|
|
160
|
-
|
|
166
|
+
const finalViewCount = Math.floor(vid.view_count);
|
|
167
|
+
if (finalViewCount < 0) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`[next-advanced-sitemap] Invalid video view_count: ${finalViewCount}. View count cannot be negative.`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
xml += ` <video:view_count>${finalViewCount}</video:view_count>
|
|
161
173
|
`;
|
|
162
174
|
}
|
|
163
175
|
if (vid.live) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/xml-escape.ts","../src/core/generator.ts","../src/index.ts"],"sourcesContent":["/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\n/**\n * Convertit les caractères spéciaux en entités XML pour éviter la corruption du fichier.\n * Gère : <, >, &, \", '\n */\nexport function escapeXml(unsafe: string | undefined | null): string {\n if (!unsafe) return '';\n \n return unsafe.replace(/[<>&\"']/g, (c) => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\"': return '"';\n case \"'\": return ''';\n default: return c;\n }\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from '../types/sitemap.js';\nimport { escapeXml } from '../utils/xml-escape.js';\n\n/**\n * Nettoie et valide de manière stricte le format et la structure d'une URL.\n * v1.0.7 : Intégration de l'Auto-Trimming (nettoyage des espaces de début et de fin)\n */\nfunction sanitizeAndValidateUrl(rawUrl: string, context: string): string {\n const url = rawUrl ? rawUrl.trim() : '';\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n throw new Error(\n `[next-advanced-sitemap] Invalid URL in ${context}: \"${url}\". URLs must start with http:// or https://`\n );\n }\n\n if (url.includes(' ')) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n let isValid = false;\n if (typeof URL.canParse === 'function') {\n isValid = URL.canParse(url);\n } else {\n try {\n new URL(url);\n isValid = true;\n } catch {\n isValid = false;\n }\n }\n\n if (!isValid) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n return url;\n}\n\n/**\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\n * v1.1.2 : Métadonnées d'Accessibilité Images (caption & title optionnels, renforcés et nettoyés)\n * + Support sécurisé des statistiques vidéo (duration & view_count)\n */\nexport function generateXml(entries: SitemapEntry[], options: SitemapOptions = {}): string {\n const now = new Date().toISOString();\n let finalEntries = [...entries];\n\n if (options.sortByPriority) {\n finalEntries.sort((a, b) => {\n const priorityA = a.priority !== undefined ? (a.priority as number) : 0.5;\n const priorityB = b.priority !== undefined ? (b.priority as number) : 0.5;\n return priorityB - priorityA;\n });\n }\n \n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\n\n for (const entry of finalEntries) {\n const cleanMainUrl = sanitizeAndValidateUrl(entry.url, 'main entry');\n\n xml += ` <url>\\n`;\n xml += ` <loc>${escapeXml(cleanMainUrl)}</loc>\\n`;\n\n if (entry.alternates?.length) {\n for (const alt of entry.alternates) {\n const cleanAltUrl = sanitizeAndValidateUrl(alt.href, 'alternate link');\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(cleanAltUrl)}\" />\\n`;\n }\n }\n\n let lastmodValue = entry.lastmod;\n if (options.autoLastmod && !lastmodValue) {\n lastmodValue = now;\n }\n\n if (lastmodValue) {\n const date = lastmodValue instanceof Date ? lastmodValue.toISOString() : lastmodValue;\n xml += ` <lastmod>${date}</lastmod>\\n`;\n }\n\n if (entry.changefreq) {\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\n }\n\n if (entry.priority !== undefined) {\n xml += ` <priority>${(entry.priority as number).toFixed(1)}</priority>\\n`;\n }\n\n // Extension Images - v1.1.2 renforcée\n if (entry.images?.length) {\n for (const img of entry.images) {\n const cleanImgUrl = sanitizeAndValidateUrl(img.loc, 'image location');\n \n xml += ` <image:image>\\n`;\n xml += ` <image:loc>${escapeXml(cleanImgUrl)}</image:loc>\\n`;\n \n // ✨ v1.1.2 : .trim() préventif pour s'assurer qu'un texte blanc ne crée pas de balise invalide\n if (img.title && img.title.trim() !== '') {\n xml += ` <image:title>${escapeXml(img.title.trim())}</image:title>\\n`;\n }\n if (img.caption && img.caption.trim() !== '') {\n xml += ` <image:caption>${escapeXml(img.caption.trim())}</image:caption>\\n`;\n }\n \n // SEO Local\n if (img.geo_location && img.geo_location.trim() !== '') {\n xml += ` <image:geo_location>${escapeXml(img.geo_location.trim())}</image:geo_location>\\n`;\n }\n \n // Gestion des Licences Google Images\n if (img.license) {\n const cleanLicenseUrl = sanitizeAndValidateUrl(img.license, 'image license URL');\n xml += ` <image:license>${escapeXml(cleanLicenseUrl)}</image:license>\\n`;\n }\n \n xml += ` </image:image>\\n`;\n }\n }\n\n // Extension Vidéos\n if (entry.videos?.length) {\n for (const vid of entry.videos) {\n const cleanThumbLoc = sanitizeAndValidateUrl(vid.thumbnail_loc, 'video thumbnail');\n const cleanContentLoc = vid.content_loc ? sanitizeAndValidateUrl(vid.content_loc, 'video content location') : undefined;\n const cleanPlayerLoc = vid.player_loc ? sanitizeAndValidateUrl(vid.player_loc, 'video player location') : undefined;\n\n xml += ` <video:video>\\n`;\n xml += ` <video:thumbnail_loc>${escapeXml(cleanThumbLoc)}</video:thumbnail_loc>\\n`;\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\n \n if (cleanContentLoc) xml += ` <video:content_loc>${escapeXml(cleanContentLoc)}</video:content_loc>\\n`;\n if (cleanPlayerLoc) xml += ` <video:player_loc>${escapeXml(cleanPlayerLoc)}</video:player_loc>\\n`;\n \n if (vid.publication_date) {\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\n }\n\n // Métadonnées de statistiques (v1.1.3 - Préparé et sécurisé)\n if (vid.duration !== undefined) {\n xml += ` <video:duration>${Math.floor(vid.duration)}</video:duration>\\n`;\n }\n if (vid.view_count !== undefined) {\n xml += ` <video:view_count>${Math.floor(vid.view_count)}</video:view_count>\\n`;\n }\n\n // Support du Live Streaming v1.1.1\n if (vid.live) {\n xml += ` <video:live>${vid.live}</video:live>\\n`;\n }\n\n xml += ` </video:video>\\n`;\n }\n }\n\n // Extension News\n if (entry.news) {\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\n xml += ` <news:news>\\n`;\n xml += ` <news:publication>\\n`;\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\n xml += ` </news:publication>\\n`;\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\n xml += ` </news:news>\\n`;\n }\n\n xml += ` </url>\\n`;\n }\n\n xml += `</urlset>`;\n return xml;\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from './types/sitemap.js';\nimport { generateXml } from './core/generator.js';\n\nexport * from './types/sitemap.js';\n\n/**\n * Génère une réponse HTTP compatible Next.js (App Router) avec options de configuration.\n * v1.0.9 : Injection dynamique et personnalisable de l'en-tête Cache-Control via l'option maxAge\n * * @param entries - Liste des entrées du sitemap\n * @param options - Options de génération et de mise en cache (ex: autoLastmod, maxAge)\n * @returns Une instance de Response contenant le flux XML configuré\n */\nexport function getServerSitemapResponse(\n entries: SitemapEntry[], \n options: SitemapOptions = {}\n): Response {\n const xml = generateXml(entries, options);\n\n // Détermination de la stratégie de mise en cache (v1.0.9)\n const cacheControlHeader = options.maxAge !== undefined\n ? `public, max-age=${options.maxAge}, must-revalidate`\n : 'public, s-maxage=86400, stale-while-revalidate';\n\n return new Response(xml, {\n headers: {\n 'Content-Type': 'application/xml',\n 'Cache-Control': cacheControlHeader,\n },\n });\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/xml-escape.ts","../src/core/generator.ts","../src/index.ts"],"sourcesContent":["/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\n/**\n * Convertit les caractères spéciaux en entités XML pour éviter la corruption du fichier.\n * Gère : <, >, &, \", '\n */\nexport function escapeXml(unsafe: string | undefined | null): string {\n if (!unsafe) return '';\n \n return unsafe.replace(/[<>&\"']/g, (c) => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\"': return '"';\n case \"'\": return ''';\n default: return c;\n }\n });\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from '../types/sitemap.js';\nimport { escapeXml } from '../utils/xml-escape.js';\n\n/**\n * Nettoie et valide de manière stricte le format et la structure d'une URL.\n * v1.0.7 : Intégration de l'Auto-Trimming (nettoyage des espaces de début et de fin)\n */\nfunction sanitizeAndValidateUrl(rawUrl: string, context: string): string {\n const url = rawUrl ? rawUrl.trim() : '';\n\n if (!url.startsWith('http://') && !url.startsWith('https://')) {\n throw new Error(\n `[next-advanced-sitemap] Invalid URL in ${context}: \"${url}\". URLs must start with http:// or https://`\n );\n }\n\n if (url.includes(' ')) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n let isValid = false;\n if (typeof URL.canParse === 'function') {\n isValid = URL.canParse(url);\n } else {\n try {\n new URL(url);\n isValid = true;\n } catch {\n isValid = false;\n }\n }\n\n if (!isValid) {\n throw new Error(\n `[next-advanced-sitemap] Malformed URL structure detected in ${context}: \"${url}\". Please verify spaces or special characters.`\n );\n }\n\n return url;\n}\n\n/**\n * Génère le flux XML complet du sitemap incluant les extensions Images, Vidéos, News et Hreflang.\n * v1.1.3 : Validation stricte des statistiques vidéo (duration entre 0-28800s & view_count >= 0)\n */\nexport function generateXml(entries: SitemapEntry[], options: SitemapOptions = {}): string {\n const now = new Date().toISOString();\n let finalEntries = [...entries];\n\n if (options.sortByPriority) {\n finalEntries.sort((a, b) => {\n const priorityA = a.priority !== undefined ? (a.priority as number) : 0.5;\n const priorityB = b.priority !== undefined ? (b.priority as number) : 0.5;\n return priorityB - priorityA;\n });\n }\n \n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n`;\n xml += `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\\n`;\n xml += ` xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\\n`;\n xml += ` xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\"\\n`;\n xml += ` xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\\n`;\n xml += ` xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\\n`;\n\n for (const entry of finalEntries) {\n const cleanMainUrl = sanitizeAndValidateUrl(entry.url, 'main entry');\n\n xml += ` <url>\\n`;\n xml += ` <loc>${escapeXml(cleanMainUrl)}</loc>\\n`;\n\n if (entry.alternates?.length) {\n for (const alt of entry.alternates) {\n const cleanAltUrl = sanitizeAndValidateUrl(alt.href, 'alternate link');\n xml += ` <xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(alt.hreflang)}\" href=\"${escapeXml(cleanAltUrl)}\" />\\n`;\n }\n }\n\n let lastmodValue = entry.lastmod;\n if (options.autoLastmod && !lastmodValue) {\n lastmodValue = now;\n }\n\n if (lastmodValue) {\n const date = lastmodValue instanceof Date ? lastmodValue.toISOString() : lastmodValue;\n xml += ` <lastmod>${date}</lastmod>\\n`;\n }\n\n if (entry.changefreq) {\n xml += ` <changefreq>${entry.changefreq}</changefreq>\\n`;\n }\n\n if (entry.priority !== undefined) {\n xml += ` <priority>${(entry.priority as number).toFixed(1)}</priority>\\n`;\n }\n\n // Extension Images\n if (entry.images?.length) {\n for (const img of entry.images) {\n const cleanImgUrl = sanitizeAndValidateUrl(img.loc, 'image location');\n \n xml += ` <image:image>\\n`;\n xml += ` <image:loc>${escapeXml(cleanImgUrl)}</image:loc>\\n`;\n \n if (img.title && img.title.trim() !== '') {\n xml += ` <image:title>${escapeXml(img.title.trim())}</image:title>\\n`;\n }\n if (img.caption && img.caption.trim() !== '') {\n xml += ` <image:caption>${escapeXml(img.caption.trim())}</image:caption>\\n`;\n }\n \n if (img.geo_location && img.geo_location.trim() !== '') {\n xml += ` <image:geo_location>${escapeXml(img.geo_location.trim())}</image:geo_location>\\n`;\n }\n \n if (img.license) {\n const cleanLicenseUrl = sanitizeAndValidateUrl(img.license, 'image license URL');\n xml += ` <image:license>${escapeXml(cleanLicenseUrl)}</image:license>\\n`;\n }\n \n xml += ` </image:image>\\n`;\n }\n }\n\n // Extension Vidéos - v1.1.3 sécurisée\n if (entry.videos?.length) {\n for (const vid of entry.videos) {\n const cleanThumbLoc = sanitizeAndValidateUrl(vid.thumbnail_loc, 'video thumbnail');\n const cleanContentLoc = vid.content_loc ? sanitizeAndValidateUrl(vid.content_loc, 'video content location') : undefined;\n const cleanPlayerLoc = vid.player_loc ? sanitizeAndValidateUrl(vid.player_loc, 'video player location') : undefined;\n\n xml += ` <video:video>\\n`;\n xml += ` <video:thumbnail_loc>${escapeXml(cleanThumbLoc)}</video:thumbnail_loc>\\n`;\n xml += ` <video:title>${escapeXml(vid.title)}</video:title>\\n`;\n xml += ` <video:description>${escapeXml(vid.description)}</video:description>\\n`;\n \n if (cleanContentLoc) xml += ` <video:content_loc>${escapeXml(cleanContentLoc)}</video:content_loc>\\n`;\n if (cleanPlayerLoc) xml += ` <video:player_loc>${escapeXml(cleanPlayerLoc)}</video:player_loc>\\n`;\n \n if (vid.publication_date) {\n const vDate = vid.publication_date instanceof Date ? vid.publication_date.toISOString() : vid.publication_date;\n xml += ` <video:publication_date>${vDate}</video:publication_date>\\n`;\n }\n\n // ✨ Validation et Sérialisation de la durée (0 - 28800s)\n if (vid.duration !== undefined) {\n const finalDuration = Math.floor(vid.duration);\n if (finalDuration < 0 || finalDuration > 28800) {\n throw new Error(\n `[next-advanced-sitemap] Invalid video duration: ${finalDuration}. Duration must be an integer between 0 and 28800 seconds (8 hours).`\n );\n }\n xml += ` <video:duration>${finalDuration}</video:duration>\\n`;\n }\n\n // ✨ Validation et Sérialisation du nombre de vues (>= 0)\n if (vid.view_count !== undefined) {\n const finalViewCount = Math.floor(vid.view_count);\n if (finalViewCount < 0) {\n throw new Error(\n `[next-advanced-sitemap] Invalid video view_count: ${finalViewCount}. View count cannot be negative.`\n );\n }\n xml += ` <video:view_count>${finalViewCount}</video:view_count>\\n`;\n }\n\n if (vid.live) {\n xml += ` <video:live>${vid.live}</video:live>\\n`;\n }\n\n xml += ` </video:video>\\n`;\n }\n }\n\n // Extension News\n if (entry.news) {\n const nDate = entry.news.publication_date instanceof Date ? entry.news.publication_date.toISOString() : entry.news.publication_date;\n xml += ` <news:news>\\n`;\n xml += ` <news:publication>\\n`;\n xml += ` <news:name>${escapeXml(entry.news.name)}</news:name>\\n`;\n xml += ` <news:language>${escapeXml(entry.news.language)}</news:language>\\n`;\n xml += ` </news:publication>\\n`;\n xml += ` <news:publication_date>${nDate}</news:publication_date>\\n`;\n xml += ` <news:title>${escapeXml(entry.news.title)}</news:title>\\n`;\n xml += ` </news:news>\\n`;\n }\n\n xml += ` </url>\\n`;\n }\n\n xml += `</urlset>`;\n return xml;\n}","/* * Copyright (c) 2026 Fordi / FomaDev. \n * Licensed under FomaDev Public License.\n * See LICENSE file in the project root for full license information.\n */\n\nimport { SitemapEntry, SitemapOptions } from './types/sitemap.js';\nimport { generateXml } from './core/generator.js';\n\nexport * from './types/sitemap.js';\n\n/**\n * Génère une réponse HTTP compatible Next.js (App Router) avec options de configuration.\n * v1.0.9 : Injection dynamique et personnalisable de l'en-tête Cache-Control via l'option maxAge\n * * @param entries - Liste des entrées du sitemap\n * @param options - Options de génération et de mise en cache (ex: autoLastmod, maxAge)\n * @returns Une instance de Response contenant le flux XML configuré\n */\nexport function getServerSitemapResponse(\n entries: SitemapEntry[], \n options: SitemapOptions = {}\n): Response {\n const xml = generateXml(entries, options);\n\n // Détermination de la stratégie de mise en cache (v1.0.9)\n const cacheControlHeader = options.maxAge !== undefined\n ? `public, max-age=${options.maxAge}, must-revalidate`\n : 'public, s-maxage=86400, stale-while-revalidate';\n\n return new Response(xml, {\n headers: {\n 'Content-Type': 'application/xml',\n 'Cache-Control': cacheControlHeader,\n },\n });\n}"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-advanced-sitemap",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Advanced sitemap generator for Next.js. Powerful support for Google Images, Video, News, and Hreflang (multilingual). Type-safe, zero-dependency, and built for App Router.",
|
|
6
6
|
"main": "./dist/index.cjs",
|