@pas7/nextjs-sitemap-hreflang 0.3.1 → 0.5.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/README.md +79 -91
- package/dist/index.d.ts +22 -14
- package/dist/index.js +53 -12
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,36 +1,53 @@
|
|
|
1
|
-
# nextjs-sitemap-hreflang
|
|
1
|
+
# @pas7/nextjs-sitemap-hreflang
|
|
2
2
|
|
|
3
3
|
[](https://github.com/pas7-studio/nextjs-sitemap-hreflang/actions/workflows/ci.yml)
|
|
4
|
-
[](https://www.npmjs.com/package/@pas7/nextjs-sitemap-hreflang)
|
|
5
|
+
[](https://www.npmjs.com/package/@pas7/nextjs-sitemap-hreflang)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
8
|
Routing-agnostic hreflang toolkit for Next.js sitemaps:
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
9
|
+
- build hreflang alternates for App Router `MetadataRoute.Sitemap`
|
|
10
|
+
- inject/fix hreflang directly in generated XML
|
|
11
|
+
- validate sitemap hreflang in CI
|
|
12
|
+
- support mixed content pipelines (`.ts`, `.json`, `.md/.mdx`, CMS)
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm i nextjs-sitemap-hreflang
|
|
17
|
+
npm i @pas7/nextjs-sitemap-hreflang
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Migration guide (unscoped -> scoped)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
```bash
|
|
23
|
+
npm uninstall nextjs-sitemap-hreflang
|
|
24
|
+
npm i @pas7/nextjs-sitemap-hreflang
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Update imports:
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
```ts
|
|
30
|
+
// before
|
|
31
|
+
import { withHreflang } from "nextjs-sitemap-hreflang";
|
|
32
|
+
|
|
33
|
+
// after
|
|
34
|
+
import { withHreflang } from "@pas7/nextjs-sitemap-hreflang";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
CLI binary stays the same:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx nextjs-sitemap-hreflang check --in public/sitemap.xml --fail-on-missing
|
|
41
|
+
```
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
## Quick start: App Router
|
|
30
44
|
|
|
31
45
|
```ts
|
|
32
46
|
import type { MetadataRoute } from "next";
|
|
33
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
withHreflangFromRouting,
|
|
49
|
+
routingPrefixAsNeeded,
|
|
50
|
+
} from "@pas7/nextjs-sitemap-hreflang";
|
|
34
51
|
|
|
35
52
|
const routing = routingPrefixAsNeeded({
|
|
36
53
|
defaultLocale: "en",
|
|
@@ -50,75 +67,62 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|
|
50
67
|
}
|
|
51
68
|
```
|
|
52
69
|
|
|
53
|
-
|
|
70
|
+
## Next.js App Router + static export (`sitemap.xml`)
|
|
54
71
|
|
|
55
|
-
|
|
72
|
+
Use library generation + XML validation in CI:
|
|
56
73
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const blogManifest = [
|
|
61
|
-
{
|
|
62
|
-
slug: "nestjs-request-context-als-2026",
|
|
63
|
-
locales: ["en", "uk", "de", "it", "hr"],
|
|
64
|
-
updatedAt: "2026-02-08",
|
|
65
|
-
},
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
const entries = createSitemapEntriesFromManifest(blogManifest, {
|
|
69
|
-
baseUrl: "https://pas7.com.ua",
|
|
70
|
-
sectionPath: "/blog",
|
|
71
|
-
defaultLocale: "en",
|
|
72
|
-
routeStyle: "locale-segment", // /blog/en/slug, /blog/uk/slug
|
|
73
|
-
});
|
|
74
|
+
```bash
|
|
75
|
+
next build
|
|
76
|
+
npx nextjs-sitemap-hreflang check --in public/sitemap.xml --fail-on-missing
|
|
74
77
|
```
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
Optional postbuild fix step:
|
|
77
80
|
|
|
78
81
|
```bash
|
|
79
82
|
npx nextjs-sitemap-hreflang inject --in public/sitemap.xml --out public/sitemap.xml
|
|
80
|
-
npx nextjs-sitemap-hreflang check --in public/sitemap.xml --fail-on-missing
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
##
|
|
85
|
+
## Universal manifest helper (`.ts` / `.json` / `.md`)
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
- `routingPrefixAsNeeded`
|
|
87
|
-
- `routingPrefixAlways`
|
|
88
|
-
- `routingSuffixLocale`
|
|
89
|
-
- `routingDomainBased`
|
|
90
|
-
- `routingPAS7`
|
|
91
|
-
- `routingCustom`
|
|
87
|
+
If your pipeline already outputs `slug + locales + date`, use:
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
`createSitemapEntriesFromManifest(items, options)` converts locale-aware manifest records into hreflang-ready entries.
|
|
96
|
-
|
|
97
|
-
Supported route styles:
|
|
98
|
-
- `prefix-as-needed`
|
|
99
|
-
- `prefix-always`
|
|
100
|
-
- `suffix-locale`
|
|
101
|
-
- `locale-segment`
|
|
89
|
+
```ts
|
|
90
|
+
import { createSitemapEntriesFromManifest } from "@pas7/nextjs-sitemap-hreflang";
|
|
102
91
|
|
|
103
|
-
|
|
92
|
+
const entries = createSitemapEntriesFromManifest(blogManifest, {
|
|
93
|
+
baseUrl: "https://pas7.com.ua",
|
|
94
|
+
sectionPath: "/blog",
|
|
95
|
+
defaultLocale: "en",
|
|
96
|
+
routeStyle: "locale-segment", // /blog/en/slug
|
|
97
|
+
});
|
|
98
|
+
```
|
|
104
99
|
|
|
105
|
-
|
|
100
|
+
## routingPAS7 with `suffixPaths` and `prefixPaths`
|
|
106
101
|
|
|
107
102
|
```ts
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
import { routingPAS7 } from "@pas7/nextjs-sitemap-hreflang";
|
|
104
|
+
|
|
105
|
+
const routing = routingPAS7({
|
|
111
106
|
defaultLocale: "en",
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
locales: ["en", "uk", "de"],
|
|
108
|
+
// /blog/uk, /contact/uk
|
|
109
|
+
suffixPaths: ["/blog", "/projects", "/services", "/cases", "/contact", "/about", "/privacy", "/terms"],
|
|
110
|
+
// /uk/about (if needed for some sections)
|
|
111
|
+
prefixPaths: ["/about"],
|
|
112
|
+
// keeps highest priority for locale-segment detail pages
|
|
113
|
+
detailPathPattern: /^\/(blog|projects|services|cases)\//,
|
|
114
114
|
});
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
Routing priority inside `routingPAS7`:
|
|
118
|
+
1. `detailPathPattern`
|
|
119
|
+
2. `suffixPaths` (or legacy `hubPaths`)
|
|
120
|
+
3. `prefixPaths`
|
|
121
|
+
4. fallback prefix-as-needed
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
## CLI
|
|
120
124
|
|
|
121
|
-
|
|
125
|
+
### inject
|
|
122
126
|
|
|
123
127
|
```bash
|
|
124
128
|
npx nextjs-sitemap-hreflang inject --in public/sitemap.xml \
|
|
@@ -128,9 +132,7 @@ npx nextjs-sitemap-hreflang inject --in public/sitemap.xml \
|
|
|
128
132
|
--trailing-slash never
|
|
129
133
|
```
|
|
130
134
|
|
|
131
|
-
###
|
|
132
|
-
|
|
133
|
-
Validate hreflang in sitemap XML.
|
|
135
|
+
### check
|
|
134
136
|
|
|
135
137
|
```bash
|
|
136
138
|
npx nextjs-sitemap-hreflang check --in public/sitemap.xml \
|
|
@@ -138,35 +140,21 @@ npx nextjs-sitemap-hreflang check --in public/sitemap.xml \
|
|
|
138
140
|
--fail-on-missing
|
|
139
141
|
```
|
|
140
142
|
|
|
141
|
-
##
|
|
142
|
-
|
|
143
|
-
```yaml
|
|
144
|
-
- name: Check sitemap hreflang
|
|
145
|
-
run: npx nextjs-sitemap-hreflang check --in public/sitemap.xml --fail-on-missing
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Release and npm Publish
|
|
149
|
-
|
|
150
|
-
This repository uses a single CI workflow (`.github/workflows/ci.yml`) with a direct release flow:
|
|
151
|
-
|
|
152
|
-
1. Add a changeset when behavior/API changes:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
npm run changeset
|
|
156
|
-
```
|
|
143
|
+
## Release and npm publish
|
|
157
144
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
- publishes to npm
|
|
145
|
+
Single workflow in `.github/workflows/ci.yml`:
|
|
146
|
+
1. add changeset (`npm run changeset`)
|
|
147
|
+
2. push to `main`
|
|
148
|
+
3. workflow runs `ci -> release`
|
|
149
|
+
4. version/tag/release/npm publish are automated
|
|
164
150
|
|
|
165
151
|
Required secret: `NPM_TOKEN`.
|
|
166
152
|
|
|
167
|
-
##
|
|
153
|
+
## Contribution policy (required)
|
|
168
154
|
|
|
169
|
-
|
|
155
|
+
- Any user-facing code change must include a changeset (`.changeset/*.md`).
|
|
156
|
+
- Any API/feature behavior change must include `README.md` updates in the same PR.
|
|
157
|
+
- CI enforces both rules on pull requests.
|
|
170
158
|
|
|
171
159
|
## Maintained by PAS7 Studio
|
|
172
160
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
type ChangeFrequency = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
2
2
|
type SitemapAlternates = {
|
|
3
|
-
languages?: Record<string, string
|
|
3
|
+
languages?: Record<string, string | undefined> | undefined;
|
|
4
4
|
};
|
|
5
5
|
type SitemapEntryLike = {
|
|
6
6
|
url: string;
|
|
7
|
-
lastModified?: string | Date;
|
|
8
|
-
changeFrequency?: ChangeFrequency;
|
|
9
|
-
priority?: number;
|
|
10
|
-
alternates?: SitemapAlternates;
|
|
11
|
-
images?: string[];
|
|
7
|
+
lastModified?: string | Date | undefined;
|
|
8
|
+
changeFrequency?: ChangeFrequency | undefined;
|
|
9
|
+
priority?: number | undefined;
|
|
10
|
+
alternates?: SitemapAlternates | undefined;
|
|
11
|
+
images?: string[] | undefined;
|
|
12
12
|
};
|
|
13
13
|
type SitemapEntry = SitemapEntryLike;
|
|
14
|
+
/**
|
|
15
|
+
* Compatibility alias for Next.js App Router MetadataRoute.Sitemap entries.
|
|
16
|
+
* Keeps alternates.languages permissive for optional values.
|
|
17
|
+
*/
|
|
18
|
+
type NextMetadataSitemapEntryCompatible = SitemapEntryLike;
|
|
14
19
|
type XDefaultStrategy = {
|
|
15
20
|
type: "loc";
|
|
16
21
|
} | {
|
|
@@ -90,7 +95,12 @@ type RoutingSuffixLocaleOptions = {
|
|
|
90
95
|
type RoutingPAS7Options = {
|
|
91
96
|
readonly defaultLocale: string;
|
|
92
97
|
readonly locales: readonly string[];
|
|
98
|
+
/**
|
|
99
|
+
* @deprecated Use suffixPaths. Kept for backward compatibility.
|
|
100
|
+
*/
|
|
93
101
|
readonly hubPaths?: readonly string[];
|
|
102
|
+
readonly suffixPaths?: readonly string[];
|
|
103
|
+
readonly prefixPaths?: readonly string[];
|
|
94
104
|
readonly detailPathPattern?: RegExp;
|
|
95
105
|
};
|
|
96
106
|
|
|
@@ -218,14 +228,12 @@ declare function routingCustom(options: {
|
|
|
218
228
|
}): RoutingStrategy;
|
|
219
229
|
/**
|
|
220
230
|
* PAS7 custom routing strategy:
|
|
221
|
-
* - Home: / (en), /uk, /de
|
|
222
|
-
* -
|
|
223
|
-
* -
|
|
231
|
+
* - Home: / (en), /uk, /de
|
|
232
|
+
* - Suffix pages: /blog (en), /blog/uk
|
|
233
|
+
* - Detail pages: /blog/en/slug, /blog/uk/slug
|
|
234
|
+
* - Prefix pages: /about (en), /uk/about
|
|
224
235
|
*
|
|
225
|
-
*
|
|
226
|
-
* - Home pages use prefix pattern for non-default locales
|
|
227
|
-
* - Hub pages (listing pages) use suffix pattern for non-default locales
|
|
228
|
-
* - Detail pages have locale as a segment after the section
|
|
236
|
+
* Priority: detailPathPattern > suffixPaths > prefixPaths > fallback.
|
|
229
237
|
*/
|
|
230
238
|
declare function routingPAS7(options: RoutingPAS7Options): RoutingStrategy;
|
|
231
239
|
|
|
@@ -258,4 +266,4 @@ type CheckXmlOptions = {
|
|
|
258
266
|
};
|
|
259
267
|
declare function checkSitemapXmlHreflang(xml: string, options: CheckXmlOptions): HreflangReport;
|
|
260
268
|
|
|
261
|
-
export { type ChangeFrequency, type CheckXmlOptions, type CreateSitemapEntriesFromManifestOptions, type ExpandLocalesOptions, type HreflangIssue, type HreflangIssueCode, type HreflangReport, type InjectOptions, type LocalizedManifestItem, type ManifestRouteStyle, type RoutingDomainBasedOptions, type RoutingPAS7Options, type RoutingPrefixAlwaysOptions, type RoutingPrefixAsNeededOptions, type RoutingStrategy, type RoutingSuffixLocaleOptions, type SitemapAlternates, type SitemapEntry, type SitemapEntryLike, type XDefaultStrategy, assertHreflangExport as assertHreflang, buildLanguagesMap, checkSitemapXmlHreflang, createSitemapEntriesFromManifest, expandLocaleEntries, injectXDefaultIntoSitemapXml, normalizeUrl, resolveAbsoluteUrl, routingCustom, routingDomainBased, routingPAS7, routingPrefixAlways, routingPrefixAsNeeded, routingSuffixLocale, withHreflang, withHreflangFromRouting };
|
|
269
|
+
export { type ChangeFrequency, type CheckXmlOptions, type CreateSitemapEntriesFromManifestOptions, type ExpandLocalesOptions, type HreflangIssue, type HreflangIssueCode, type HreflangReport, type InjectOptions, type LocalizedManifestItem, type ManifestRouteStyle, type NextMetadataSitemapEntryCompatible, type RoutingDomainBasedOptions, type RoutingPAS7Options, type RoutingPrefixAlwaysOptions, type RoutingPrefixAsNeededOptions, type RoutingStrategy, type RoutingSuffixLocaleOptions, type SitemapAlternates, type SitemapEntry, type SitemapEntryLike, type XDefaultStrategy, assertHreflangExport as assertHreflang, buildLanguagesMap, checkSitemapXmlHreflang, createSitemapEntriesFromManifest, expandLocaleEntries, injectXDefaultIntoSitemapXml, normalizeUrl, resolveAbsoluteUrl, routingCustom, routingDomainBased, routingPAS7, routingPrefixAlways, routingPrefixAsNeeded, routingSuffixLocale, withHreflang, withHreflangFromRouting };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ function assertHreflang(entries, options) {
|
|
|
7
7
|
for (const entry of entries) {
|
|
8
8
|
const languages = entry.alternates?.languages;
|
|
9
9
|
if (!languages) continue;
|
|
10
|
-
const pairs = Object.entries(languages);
|
|
10
|
+
const pairs = Object.entries(languages).filter(([, href]) => Boolean(href));
|
|
11
11
|
if (pairs.length === 0) {
|
|
12
12
|
issues.push({
|
|
13
13
|
code: "MISSING_LANGUAGES",
|
|
@@ -164,6 +164,7 @@ function normalizeEntryUrl(url, baseUrl, ensureAbsolute, trailingSlash) {
|
|
|
164
164
|
function normalizeLanguages(languages, baseUrl, ensureAbsolute, trailingSlash) {
|
|
165
165
|
const out = {};
|
|
166
166
|
for (const [k, v] of Object.entries(languages)) {
|
|
167
|
+
if (!v) continue;
|
|
167
168
|
const abs = ensureAbsolute ? resolveAbsoluteUrl(v, baseUrl ?? v) : v;
|
|
168
169
|
out[k] = normalizeUrl(abs, trailingSlash);
|
|
169
170
|
}
|
|
@@ -293,39 +294,79 @@ function routingPAS7(options) {
|
|
|
293
294
|
defaultLocale,
|
|
294
295
|
locales,
|
|
295
296
|
hubPaths = ["/blog", "/projects", "/services", "/cases"],
|
|
297
|
+
suffixPaths,
|
|
298
|
+
prefixPaths = [],
|
|
296
299
|
detailPathPattern = /^\/(blog|projects|services|cases)\//
|
|
297
300
|
} = options;
|
|
301
|
+
const normalizedSuffixPaths = uniquePaths(suffixPaths ?? hubPaths);
|
|
302
|
+
const normalizedPrefixPaths = uniquePaths(prefixPaths);
|
|
303
|
+
const localeSet = new Set(locales);
|
|
298
304
|
return {
|
|
299
305
|
locales,
|
|
300
306
|
canonicalLocale: defaultLocale,
|
|
301
307
|
hrefFor: ({ pathname, locale }) => {
|
|
302
|
-
const normalizedPath = pathname
|
|
303
|
-
if (normalizedPath === "/"
|
|
308
|
+
const normalizedPath = normalizeInputPath(pathname);
|
|
309
|
+
if (normalizedPath === "/") {
|
|
304
310
|
if (locale === defaultLocale) return "/";
|
|
305
311
|
return `/${locale}`;
|
|
306
312
|
}
|
|
307
313
|
if (detailPathPattern.test(normalizedPath)) {
|
|
308
314
|
const parts = normalizedPath.split("/").filter(Boolean);
|
|
309
|
-
if (parts.length >= 3) {
|
|
315
|
+
if (parts.length >= 3 && localeSet.has(parts[1] ?? "")) {
|
|
310
316
|
const section = parts[0];
|
|
311
317
|
const slug = parts.slice(2).join("/");
|
|
312
318
|
return `/${section}/${locale}/${slug}`;
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return
|
|
321
|
+
const suffixBase = findSuffixBasePath(normalizedPath, normalizedSuffixPaths, localeSet);
|
|
322
|
+
if (suffixBase) {
|
|
323
|
+
if (locale === defaultLocale) return suffixBase;
|
|
324
|
+
return `${suffixBase}/${locale}`;
|
|
325
|
+
}
|
|
326
|
+
const prefixBase = findPrefixBasePath(normalizedPath, normalizedPrefixPaths, localeSet);
|
|
327
|
+
if (prefixBase) {
|
|
328
|
+
if (locale === defaultLocale) return prefixBase;
|
|
329
|
+
return `/${locale}${prefixBase}`;
|
|
323
330
|
}
|
|
324
331
|
if (locale === defaultLocale) return normalizedPath;
|
|
325
332
|
return `/${locale}${normalizedPath}`;
|
|
326
333
|
}
|
|
327
334
|
};
|
|
328
335
|
}
|
|
336
|
+
function normalizeInputPath(pathname) {
|
|
337
|
+
const withLeadingSlash = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
338
|
+
if (withLeadingSlash.length > 1 && withLeadingSlash.endsWith("/")) {
|
|
339
|
+
return withLeadingSlash.slice(0, -1);
|
|
340
|
+
}
|
|
341
|
+
return withLeadingSlash;
|
|
342
|
+
}
|
|
343
|
+
function uniquePaths(paths) {
|
|
344
|
+
return [...new Set(paths.map(normalizeInputPath))];
|
|
345
|
+
}
|
|
346
|
+
function findSuffixBasePath(pathname, suffixPaths, localeSet) {
|
|
347
|
+
for (const suffixPath of suffixPaths) {
|
|
348
|
+
if (pathname === suffixPath) return suffixPath;
|
|
349
|
+
if (!pathname.startsWith(`${suffixPath}/`)) continue;
|
|
350
|
+
const rest = pathname.slice(`${suffixPath}/`.length);
|
|
351
|
+
if (!rest) continue;
|
|
352
|
+
const [maybeLocale, ...tail] = rest.split("/");
|
|
353
|
+
if (tail.length === 0 && localeSet.has(maybeLocale ?? "")) {
|
|
354
|
+
return suffixPath;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
function findPrefixBasePath(pathname, prefixPaths, localeSet) {
|
|
360
|
+
for (const prefixPath of prefixPaths) {
|
|
361
|
+
if (pathname === prefixPath) return prefixPath;
|
|
362
|
+
const withoutLeadingSlash = pathname.replace(/^\/+/, "");
|
|
363
|
+
const [maybeLocale, ...tail] = withoutLeadingSlash.split("/");
|
|
364
|
+
if (!localeSet.has(maybeLocale ?? "")) continue;
|
|
365
|
+
const candidate = normalizeInputPath(tail.join("/"));
|
|
366
|
+
if (candidate === prefixPath) return prefixPath;
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
329
370
|
|
|
330
371
|
// src/lib/expandLocales.ts
|
|
331
372
|
function expandLocaleEntries(entries, options) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/assertHreflang.ts","../src/lib/url.ts","../src/lib/buildLanguagesMap.ts","../src/lib/withHreflang.ts","../src/lib/routing.ts","../src/lib/expandLocales.ts","../src/lib/fromManifest.ts","../src/xml/xml.ts","../src/xml/inject.ts","../src/xml/check.ts"],"sourcesContent":["import type { HreflangIssue, HreflangReport, SitemapEntryLike } from \"./types.js\";\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflang(\n entries: readonly SitemapEntryLike[],\n options: AssertHreflangOptions,\n): HreflangReport {\n const requireAbsolute = options.requireAbsolute ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireSelf = options.requireSelf ?? false;\n\n const issues: HreflangIssue[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages) continue;\n\n const pairs = Object.entries(languages);\n\n if (pairs.length === 0) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: entry.url,\n message: \"alternates.languages is empty\",\n });\n continue;\n }\n\n if (requireXDefaultWhenMultiple && pairs.length > 1 && !languages[\"x-default\"]) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: entry.url,\n message: \"Missing x-default hreflang for a multilingual entry\",\n });\n }\n\n if (requireSelf && options.canonicalLocale) {\n if (!languages[options.canonicalLocale]) {\n issues.push({\n code: \"MISSING_SELF\",\n entryUrl: entry.url,\n message: `Missing self hreflang for canonicalLocale=${options.canonicalLocale}`,\n });\n }\n }\n\n const hrefSeen = new Set<string>();\n for (const [locale, href] of pairs) {\n if (!isValidLocaleKey(locale)) {\n issues.push({\n code: \"INVALID_LOCALE_KEY\",\n entryUrl: entry.url,\n message: `Invalid locale key: ${locale}`,\n });\n }\n if (requireAbsolute && !isAbsoluteUrl(href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: entry.url,\n message: `Non-absolute hreflang href for ${locale}: ${href}`,\n });\n }\n if (hrefSeen.has(href)) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: entry.url,\n message: `Duplicate hreflang href detected: ${href}`,\n });\n }\n hrefSeen.add(href);\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n return url.startsWith(\"http://\") || url.startsWith(\"https://\");\n}\n\nfunction isValidLocaleKey(key: string): boolean {\n if (key === \"x-default\") return true;\n return /^[a-z]{2}(-[A-Z]{2})?$/.test(key);\n}\n","export type TrailingSlashPolicy = \"preserve\" | \"always\" | \"never\";\n\nexport function normalizeUrl(url: string, trailingSlash: TrailingSlashPolicy): string {\n const u = new URL(url);\n if (trailingSlash === \"preserve\") return u.toString();\n if (trailingSlash === \"always\") {\n if (!u.pathname.endsWith(\"/\")) u.pathname = `${u.pathname}/`;\n return u.toString();\n }\n if (u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) u.pathname = u.pathname.slice(0, -1);\n return u.toString();\n}\n\nexport function resolveAbsoluteUrl(input: string, baseUrl: string): string {\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) return input;\n return new URL(input.startsWith(\"/\") ? input : `/${input}`, baseUrl).toString();\n}\n\nexport function getOriginFromAbsoluteUrl(absoluteUrl: string): string {\n const u = new URL(absoluteUrl);\n return `${u.protocol}//${u.host}`;\n}\n","import type { XDefaultStrategy } from \"./types.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type BuildLanguagesMapInput = {\n baseUrl: string;\n locales: readonly string[];\n canonicalLocale: string;\n resolveHref: (locale: string) => string;\n includeXDefault: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport type BuildLanguagesMapOutput = {\n canonical: string;\n languages: Record<string, string>;\n};\n\nexport function buildLanguagesMap(input: BuildLanguagesMapInput): BuildLanguagesMapOutput {\n const trailingSlash = input.trailingSlash ?? \"preserve\";\n const languages: Record<string, string> = {};\n\n for (const locale of input.locales) {\n const href = input.resolveHref(locale);\n const abs = resolveAbsoluteUrl(href, input.baseUrl);\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n const canonical = languages[input.canonicalLocale] ?? normalizeUrl(input.baseUrl, trailingSlash);\n\n if (input.includeXDefault) {\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: input.baseUrl,\n strategy: input.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n return { canonical, languages };\n}\n\nexport function resolveXDefaultForLanguages(args: {\n canonical: string;\n languages: Record<string, string>;\n baseUrl: string;\n strategy: XDefaultStrategy;\n trailingSlash: \"preserve\" | \"always\" | \"never\";\n}): string {\n const { canonical, languages, baseUrl, strategy, trailingSlash } = args;\n\n if (strategy.type === \"loc\") return normalizeUrl(canonical, trailingSlash);\n if (strategy.type === \"root\") return normalizeUrl(resolveAbsoluteUrl(\"/\", baseUrl), trailingSlash);\n if (strategy.type === \"custom\") return normalizeUrl(resolveAbsoluteUrl(strategy.url, baseUrl), trailingSlash);\n if (strategy.type === \"locale\") {\n const href = languages[strategy.locale];\n if (href) return normalizeUrl(href, trailingSlash);\n return normalizeUrl(canonical, trailingSlash);\n }\n const href = strategy.resolve({ url: canonical });\n return normalizeUrl(resolveAbsoluteUrl(href, baseUrl), trailingSlash);\n}\n","import type {\n HreflangReport,\n SitemapEntryLike,\n XDefaultStrategy,\n RoutingStrategy,\n} from \"./types.js\";\nimport { assertHreflang as assertHreflangImpl } from \"./assertHreflang.js\";\nimport { buildLanguagesMap, resolveXDefaultForLanguages } from \"./buildLanguagesMap.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport { normalizeUrl, resolveAbsoluteUrl };\nexport { buildLanguagesMap };\n\nexport type WithHreflangOptions = {\n baseUrl?: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n canonicalLocale?: string;\n xDefaultStrategy?: XDefaultStrategy;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n};\n\nexport function withHreflang<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: WithHreflangOptions,\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n\n const languages = entry.alternates?.languages\n ? normalizeLanguages(entry.alternates.languages, options.baseUrl, ensureAbsolute, trailingSlash)\n : undefined;\n\n if (!languages) {\n return { ...entry, url } as T;\n }\n\n const nextLanguages = { ...languages };\n\n if (ensureSelf && options.canonicalLocale) {\n if (!nextLanguages[options.canonicalLocale]) nextLanguages[options.canonicalLocale] = url;\n }\n\n if (ensureXDefault) {\n if (!nextLanguages[\"x-default\"]) {\n const xDefault = resolveXDefaultForLanguages({\n canonical: url,\n languages: nextLanguages,\n baseUrl: options.baseUrl ?? url,\n strategy: options.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n nextLanguages[\"x-default\"] = xDefault;\n }\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages: nextLanguages };\n\n return { ...entry, url, alternates } as T;\n });\n}\n\nfunction normalizeEntryUrl(\n url: string,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): string {\n const abs = ensureAbsolute ? resolveAbsoluteUrl(url, baseUrl ?? url) : url;\n return normalizeUrl(abs, trailingSlash);\n}\n\nfunction normalizeLanguages(\n languages: Record<string, string>,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(languages)) {\n const abs = ensureAbsolute ? resolveAbsoluteUrl(v, baseUrl ?? v) : v;\n out[k] = normalizeUrl(abs, trailingSlash);\n }\n return out;\n}\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflangExport(entries: readonly SitemapEntryLike[], options: AssertHreflangOptions): HreflangReport {\n return assertHreflangImpl(entries, options);\n}\n\n/**\n * Build hreflang entries using a routing strategy.\n * This is the recommended way to use withHreflang with i18n routing.\n */\nexport function withHreflangFromRouting<T extends SitemapEntryLike>(\n entries: readonly T[],\n strategy: RoutingStrategy,\n options: {\n baseUrl: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n },\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n // Extract pathname from URL\n let pathname: string;\n try {\n const url = new URL(entry.url, options.baseUrl);\n pathname = url.pathname;\n } catch {\n pathname = entry.url;\n }\n\n // Build languages map from routing strategy\n const languages: Record<string, string> = {};\n for (const locale of strategy.locales) {\n const href = strategy.hrefFor({ pathname, locale });\n const abs = ensureAbsolute ? resolveAbsoluteUrl(href, options.baseUrl) : href;\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n // Add x-default if needed\n if (ensureXDefault && !languages[\"x-default\"]) {\n const canonical =\n languages[strategy.canonicalLocale] ?? normalizeUrl(entry.url, trailingSlash);\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: options.baseUrl,\n strategy: strategy.xDefault ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n // Ensure self for canonical locale\n if (ensureSelf && !languages[strategy.canonicalLocale]) {\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n languages[strategy.canonicalLocale] = url;\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages };\n\n return { ...entry, alternates } as T;\n });\n}\n","import type {\n RoutingStrategy,\n RoutingPrefixAsNeededOptions,\n RoutingPrefixAlwaysOptions,\n RoutingDomainBasedOptions,\n RoutingSuffixLocaleOptions,\n RoutingPAS7Options,\n} from \"./types.js\";\n\n/**\n * Prefix-as-needed routing: default locale has no prefix, others have /locale\n * Example: /about (en), /uk/about (uk), /de/about (de)\n *\n * Compatible with next-intl's prefix-as-needed strategy.\n */\nexport function routingPrefixAsNeeded(\n options: RoutingPrefixAsNeededOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n // Default locale - no prefix\n return `${base}${normalizedPath}`;\n }\n // Other locales - add prefix\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Prefix-always routing: all locales have /locale prefix\n * Example: /en/about, /uk/about, /de/about\n *\n * Compatible with next-intl's prefix-always strategy.\n */\nexport function routingPrefixAlways(\n options: RoutingPrefixAlwaysOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Domain-based routing: each locale has its own domain\n * Example: example.com (en), example.de (de), example.ua (uk)\n *\n * Compatible with next-intl's domain-based routing.\n */\nexport function routingDomainBased(\n options: RoutingDomainBasedOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, localeToDomain } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const domain = localeToDomain[locale];\n if (!domain) {\n // Fallback to default domain with prefix\n const defaultDomain = localeToDomain[defaultLocale] || \"\";\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${defaultDomain}/${locale}${normalizedPath}`;\n }\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${domain}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Suffix-locale routing: locale added as suffix to path\n * Example: /blog (en), /blog/uk (uk), /blog/de (de)\n *\n * Common for blogs and content sections.\n */\nexport function routingSuffixLocale(\n options: RoutingSuffixLocaleOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n // Remove trailing slash for consistent handling\n const path =\n normalizedPath.endsWith(\"/\") && normalizedPath !== \"/\"\n ? normalizedPath.slice(0, -1)\n : normalizedPath;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n // Default locale - no suffix\n return `${base}${path}`;\n }\n // Other locales - add suffix\n return `${base}${path}/${locale}`;\n },\n };\n}\n\n/**\n * Custom routing with user-defined function\n */\nexport function routingCustom(options: {\n locales: readonly string[];\n canonicalLocale: string;\n hrefFor: (args: { pathname: string; locale: string }) => string;\n xDefault?: RoutingStrategy[\"xDefault\"];\n}): RoutingStrategy {\n if (options.xDefault !== undefined) {\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n xDefault: options.xDefault,\n };\n }\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n };\n}\n\n/**\n * PAS7 custom routing strategy:\n * - Home: / (en), /uk, /de, /it, /hr\n * - Hubs: /blog (en), /blog/uk, /blog/de\n * - Details: /blog/en/<slug>, /blog/uk/<slug>\n *\n * This is a hybrid routing scheme where:\n * - Home pages use prefix pattern for non-default locales\n * - Hub pages (listing pages) use suffix pattern for non-default locales\n * - Detail pages have locale as a segment after the section\n */\nexport function routingPAS7(options: RoutingPAS7Options): RoutingStrategy {\n const {\n defaultLocale,\n locales,\n hubPaths = [\"/blog\", \"/projects\", \"/services\", \"/cases\"],\n detailPathPattern = /^\\/(blog|projects|services|cases)\\//,\n } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n // Normalize pathname\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n\n // Home page\n if (normalizedPath === \"/\" || normalizedPath === \"\") {\n if (locale === defaultLocale) return \"/\";\n return `/${locale}`;\n }\n\n // Detail pages - locale is segment after section\n // /blog/en/slug → /blog/uk/slug\n if (detailPathPattern.test(normalizedPath)) {\n const parts = normalizedPath.split(\"/\").filter(Boolean);\n if (parts.length >= 3) {\n // parts[0] = section, parts[1] = locale, parts[2+] = slug\n const section = parts[0];\n const slug = parts.slice(2).join(\"/\");\n return `/${section}/${locale}/${slug}`;\n }\n }\n\n // Hub pages - locale as suffix\n // /blog → /blog/uk\n const matchingHub = hubPaths.find((hub) => {\n const normalizedHub = hub.startsWith(\"/\") ? hub : `/${hub}`;\n return normalizedPath === normalizedHub || normalizedPath === `${normalizedHub}/`;\n });\n if (matchingHub) {\n const normalizedHub = matchingHub.startsWith(\"/\")\n ? matchingHub.replace(/\\/$/, \"\")\n : `/${matchingHub.replace(/\\/$/, \"\")}`;\n if (locale === defaultLocale) return normalizedHub;\n return `${normalizedHub}/${locale}`;\n }\n\n // Fallback: prefix-as-needed style\n if (locale === defaultLocale) return normalizedPath;\n return `/${locale}${normalizedPath}`;\n },\n };\n}\n","import type { SitemapEntryLike } from \"./types.js\";\n\nexport type ExpandLocalesOptions = {\n mode: \"cluster-only\" | \"expanded\";\n includeXDefault?: boolean;\n locales?: readonly string[];\n};\n\nexport function expandLocaleEntries<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: ExpandLocalesOptions,\n): T[] {\n if (options.mode === \"cluster-only\") {\n return [...entries];\n }\n\n const result: T[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages || Object.keys(languages).length === 0) {\n result.push(entry);\n continue;\n }\n\n // Create a separate entry for each locale\n for (const [locale, href] of Object.entries(languages)) {\n if (locale === \"x-default\" && !options.includeXDefault) continue;\n\n // Filter by specific locales if provided\n if (options.locales && !options.locales.includes(locale)) continue;\n\n const expandedEntry: T = {\n ...entry,\n url: href,\n // alternates.languages remains the full cluster\n };\n result.push(expandedEntry);\n }\n }\n\n // Dedupe by url\n const seen = new Set<string>();\n return result.filter((e) => {\n if (seen.has(e.url)) return false;\n seen.add(e.url);\n return true;\n });\n}\n","import type { ChangeFrequency, SitemapEntry, XDefaultStrategy } from \"./types.js\";\nimport { withHreflang } from \"./withHreflang.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type ManifestRouteStyle =\n | \"prefix-as-needed\"\n | \"prefix-always\"\n | \"suffix-locale\"\n | \"locale-segment\";\n\nexport type LocalizedManifestItem = {\n slug: string;\n locales: readonly string[];\n updatedAt?: string | Date;\n publishedAt?: string | Date;\n lastModified?: string | Date;\n date?: string | Date;\n changeFrequency?: ChangeFrequency;\n priority?: number;\n images?: string[];\n};\n\nexport type CreateSitemapEntriesFromManifestOptions = {\n baseUrl: string;\n sectionPath: string;\n defaultLocale: string;\n canonicalLocale?: string;\n routeStyle?: ManifestRouteStyle;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureXDefault?: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n includeLocales?: readonly string[];\n pathnameFor?: (args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n }) => string;\n};\n\nexport function createSitemapEntriesFromManifest<T extends LocalizedManifestItem>(\n items: readonly T[],\n options: CreateSitemapEntriesFromManifestOptions,\n): SitemapEntry[] {\n const routeStyle = options.routeStyle ?? \"locale-segment\";\n const includeXDefault = options.ensureXDefault ?? true;\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const includeLocales = options.includeLocales ? new Set(options.includeLocales) : null;\n\n const entries: SitemapEntry[] = [];\n\n for (const item of items) {\n const slug = normalizeSlug(item.slug);\n if (!slug) continue;\n\n const locales = unique(item.locales).filter((locale) => (includeLocales ? includeLocales.has(locale) : true));\n if (locales.length === 0) continue;\n\n const configuredCanonical = options.canonicalLocale ?? options.defaultLocale;\n const effectiveCanonicalLocale =\n locales.includes(configuredCanonical) ? configuredCanonical : locales[0] ?? configuredCanonical;\n\n const languages: Record<string, string> = {};\n for (const locale of locales) {\n const pathname =\n options.pathnameFor?.({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n }) ??\n buildPathname({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n });\n\n const href = normalizeUrl(resolveAbsoluteUrl(pathname, options.baseUrl), trailingSlash);\n languages[locale] = href;\n }\n\n const canonicalUrl =\n languages[effectiveCanonicalLocale] ??\n normalizeUrl(resolveAbsoluteUrl(\"/\", options.baseUrl), trailingSlash);\n\n const lastModified = pickLastModified(item);\n\n const baseEntry: SitemapEntry = {\n url: canonicalUrl,\n alternates: { languages },\n ...(lastModified ? { lastModified } : {}),\n ...(item.changeFrequency ? { changeFrequency: item.changeFrequency } : {}),\n ...(typeof item.priority === \"number\" ? { priority: item.priority } : {}),\n ...(item.images ? { images: [...item.images] } : {}),\n };\n\n const [entryWithHreflang] = withHreflang([baseEntry], {\n baseUrl: options.baseUrl,\n trailingSlash,\n ensureAbsolute: true,\n ensureXDefault: includeXDefault,\n ensureSelf: true,\n canonicalLocale: effectiveCanonicalLocale,\n ...(options.xDefaultStrategy ? { xDefaultStrategy: options.xDefaultStrategy } : {}),\n });\n\n if (entryWithHreflang) entries.push(entryWithHreflang);\n }\n\n return entries;\n}\n\nfunction buildPathname(args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n}): string {\n const section = normalizeSection(args.sectionPath);\n\n if (args.routeStyle === \"prefix-as-needed\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"prefix-always\") {\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"suffix-locale\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(section, args.slug, args.locale);\n }\n\n return joinPath(section, args.locale, args.slug);\n}\n\nfunction normalizeSlug(slug: string): string {\n return slug.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizeSection(sectionPath: string): string {\n const normalized = sectionPath.trim();\n if (!normalized || normalized === \"/\") return \"\";\n return `/${normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\nfunction joinPath(...parts: string[]): string {\n const cleaned = parts\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => part.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\"));\n\n if (cleaned.length === 0) return \"/\";\n return `/${cleaned.join(\"/\")}`;\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction pickLastModified(item: LocalizedManifestItem): string | Date | undefined {\n return item.lastModified ?? item.updatedAt ?? item.publishedAt ?? item.date;\n}\n","export function xmlEscape(input: string): string {\n return input\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nexport function hasXhtmlNamespace(xml: string): boolean {\n return /<urlset[^>]*\\sxmlns:xhtml=\"http:\\/\\/www\\.w3\\.org\\/1999\\/xhtml\"[^>]*>/.test(xml);\n}\n\nexport function ensureXhtmlNamespace(xml: string): string {\n if (hasXhtmlNamespace(xml)) return xml;\n return xml.replace(\n /<urlset(\\s[^>]*?)?>/m,\n (m) => (m.includes(\"xmlns:xhtml=\") ? m : m.replace(\"<urlset\", '<urlset xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"')),\n );\n}\n\nexport function extractUrlBlocks(xml: string): string[] {\n return xml.match(/<url>[\\s\\S]*?<\\/url>/g) ?? [];\n}\n\nexport function extractLoc(urlBlock: string): string | null {\n return urlBlock.match(/<loc>([^<]+)<\\/loc>/)?.[1]?.trim() ?? null;\n}\n\nexport function extractXhtmlLinks(urlBlock: string): Array<{ hreflang: string; href: string }> {\n const out: Array<{ hreflang: string; href: string }> = [];\n const re = /<xhtml:link[^>]*\\shreflang=\"([^\"]+)\"[^>]*\\shref=\"([^\"]+)\"[^>]*\\/>/g;\n for (const m of urlBlock.matchAll(re)) {\n if (m[1] !== undefined && m[2] !== undefined) {\n out.push({ hreflang: m[1], href: m[2] });\n }\n }\n return out;\n}\n\nexport function hasXDefault(urlBlock: string): boolean {\n return /<xhtml:link[^>]*\\shreflang=\"x-default\"[^>]*\\/>/.test(urlBlock);\n}\n\nexport function insertXhtmlLink(urlBlock: string, linkXml: string): string {\n const lastLinkIdx = urlBlock.lastIndexOf(\"<xhtml:link\");\n if (lastLinkIdx === -1) {\n const locEnd = urlBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n const insertPos = locEnd + \"</loc>\".length;\n return `${urlBlock.slice(0, insertPos)}\\n ${linkXml}${urlBlock.slice(insertPos)}`;\n }\n const lineEnd = urlBlock.indexOf(\"\\n\", lastLinkIdx);\n if (lineEnd === -1) return `${urlBlock}\\n ${linkXml}`;\n return `${urlBlock.slice(0, lineEnd)}\\n ${linkXml}${urlBlock.slice(lineEnd)}`;\n}\n\nexport type ReorderOptions = {\n canonicalLocale?: string;\n order: \"canonical-first\" | \"preserve\";\n};\n\nexport function reorderXhtmlLinks(urlBlock: string, options: ReorderOptions): string {\n if (options.order === \"preserve\") return urlBlock;\n\n const links = extractXhtmlLinks(urlBlock);\n if (links.length === 0) return urlBlock;\n\n const loc = extractLoc(urlBlock);\n\n // Determine canonical\n let canonical: { hreflang: string; href: string } | undefined;\n if (options.canonicalLocale) {\n canonical = links.find((l) => l.hreflang === options.canonicalLocale);\n }\n if (!canonical && loc) {\n canonical = links.find((l) => l.href === loc);\n }\n\n // Split into groups\n const canonicalLinks = canonical ? [canonical] : [];\n const otherLinks = links.filter((l) => l !== canonical && l.hreflang !== \"x-default\");\n const xDefaultLinks = links.filter((l) => l.hreflang === \"x-default\");\n\n // Build new order\n const orderedLinks = [...canonicalLinks, ...otherLinks, ...xDefaultLinks];\n\n // Remove all existing xhtml:link and insert in new order\n const newBlock = urlBlock.replace(/<xhtml:link[^>]*\\/>\\s*/g, \"\");\n\n // Find position after </loc>\n const locEnd = newBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n\n const insertPos = locEnd + \"</loc>\".length;\n const linksXml = orderedLinks\n .map((l) => `\\n <xhtml:link rel=\"alternate\" hreflang=\"${xmlEscape(l.hreflang)}\" href=\"${xmlEscape(l.href)}\" />`)\n .join(\"\");\n\n return `${newBlock.slice(0, insertPos)}${linksXml}\\n ${newBlock.slice(insertPos).trimStart()}`;\n}\n\nexport function normalizeTrailingSlashInBlock(\n urlBlock: string,\n policy: \"preserve\" | \"always\" | \"never\",\n): string {\n if (policy === \"preserve\") return urlBlock;\n\n // Normalize <loc>\n let result = urlBlock.replace(/<loc>([^<]+)<\\/loc>/g, (_, url: string) => {\n return `<loc>${applyTrailingSlashPolicy(url, policy)}</loc>`;\n });\n\n // Normalize href in xhtml:link\n result = result.replace(\n /(<xhtml:link[^>]*href=\")([^\"]+)(\"[^>]*\\/>)/g,\n (_, prefix: string, url: string, suffix: string) => {\n return `${prefix}${applyTrailingSlashPolicy(url, policy)}${suffix}`;\n },\n );\n\n return result;\n}\n\nfunction applyTrailingSlashPolicy(url: string, policy: \"always\" | \"never\"): string {\n try {\n const u = new URL(url);\n if (policy === \"always\" && !u.pathname.endsWith(\"/\")) {\n u.pathname += \"/\";\n } else if (policy === \"never\" && u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) {\n u.pathname = u.pathname.slice(0, -1);\n }\n return u.toString();\n } catch {\n return url;\n }\n}\n","import { getOriginFromAbsoluteUrl, resolveAbsoluteUrl } from \"../lib/url.js\";\nimport type { XDefaultStrategy } from \"../lib/types.js\";\nimport {\n ensureXhtmlNamespace,\n extractLoc,\n extractUrlBlocks,\n extractXhtmlLinks,\n hasXDefault,\n insertXhtmlLink,\n normalizeTrailingSlashInBlock,\n reorderXhtmlLinks,\n xmlEscape,\n} from \"./xml.js\";\n\nexport type InjectOptions = {\n baseUrl?: string;\n xDefaultStrategy?: XDefaultStrategy;\n ensureNamespace?: boolean;\n canonicalLocale?: string;\n order?: \"canonical-first\" | \"preserve\";\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport function injectXDefaultIntoSitemapXml(xml: string, options: InjectOptions): string {\n const ensureNamespace = options.ensureNamespace ?? true;\n const xDefaultStrategy = options.xDefaultStrategy ?? { type: \"loc\" };\n const order = options.order ?? \"preserve\";\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n\n const xmlWithNs = ensureNamespace ? ensureXhtmlNamespace(xml) : xml;\n\n const blocks = extractUrlBlocks(xmlWithNs);\n if (blocks.length === 0) return xmlWithNs;\n\n let out = xmlWithNs;\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n let nextBlock = block;\n\n if (!hasXDefault(block)) {\n const href = resolveXDefaultHref({\n loc,\n links,\n ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),\n strategy: xDefaultStrategy,\n });\n\n const linkXml = `<xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"${xmlEscape(href)}\" />`;\n nextBlock = insertXhtmlLink(nextBlock, linkXml);\n }\n\n // Apply reordering if needed\n if (order === \"canonical-first\") {\n nextBlock = reorderXhtmlLinks(nextBlock, {\n ...(options.canonicalLocale ? { canonicalLocale: options.canonicalLocale } : {}),\n order: \"canonical-first\",\n });\n }\n\n // Apply trailing slash normalization\n if (trailingSlash !== \"preserve\") {\n nextBlock = normalizeTrailingSlashInBlock(nextBlock, trailingSlash);\n }\n\n out = out.replace(block, nextBlock);\n }\n\n return out;\n}\n\nfunction resolveXDefaultHref(args: {\n loc: string;\n links: Array<{ hreflang: string; href: string }>;\n baseUrl?: string;\n strategy: XDefaultStrategy;\n}): string {\n const { loc, links, baseUrl, strategy } = args;\n\n const locAbs = baseUrl ? resolveAbsoluteUrl(loc, baseUrl) : loc;\n const origin = isAbsolute(locAbs) ? getOriginFromAbsoluteUrl(locAbs) : baseUrl;\n\n if (strategy.type === \"loc\") return locAbs;\n if (strategy.type === \"root\") {\n if (!origin) return locAbs;\n return resolveAbsoluteUrl(\"/\", origin);\n }\n if (strategy.type === \"custom\") {\n if (!origin) return strategy.url;\n return resolveAbsoluteUrl(strategy.url, origin);\n }\n if (strategy.type === \"locale\") {\n const found = links.find((l) => l.hreflang === strategy.locale)?.href;\n if (found) return baseUrl ? resolveAbsoluteUrl(found, baseUrl) : found;\n return locAbs;\n }\n const computed = strategy.resolve({ url: locAbs });\n if (!origin) return computed;\n return resolveAbsoluteUrl(computed, origin);\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n","import type { HreflangIssue, HreflangReport } from \"../lib/types.js\";\nimport { extractLoc, extractUrlBlocks, extractXhtmlLinks, hasXhtmlNamespace } from \"./xml.js\";\n\nexport type CheckXmlOptions = {\n requireNamespace?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireAbsolute?: boolean;\n checkDuplicateKeys?: boolean;\n checkDuplicateHrefs?: boolean;\n checkHreflangCasing?: boolean;\n originPolicy?: \"same\" | \"allowlist\" | \"off\";\n allowedOrigins?: string[];\n};\n\nexport function checkSitemapXmlHreflang(xml: string, options: CheckXmlOptions): HreflangReport {\n const requireNamespace = options.requireNamespace ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireAbsolute = options.requireAbsolute ?? true;\n const checkDuplicateKeys = options.checkDuplicateKeys ?? true;\n const checkDuplicateHrefs = options.checkDuplicateHrefs ?? true;\n const checkHreflangCasing = options.checkHreflangCasing ?? true;\n const originPolicy = options.originPolicy ?? \"off\";\n\n const issues: HreflangIssue[] = [];\n\n const blocks = extractUrlBlocks(xml);\n\n if (requireNamespace) {\n const usesXhtml = /<xhtml:link\\b/.test(xml);\n if (usesXhtml && !hasXhtmlNamespace(xml)) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: \"sitemap.xml\",\n message: 'Missing xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" in <urlset>',\n });\n }\n }\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n const hasXDefault = links.some((l) => l.hreflang === \"x-default\");\n if (requireXDefaultWhenMultiple && links.length > 1 && !hasXDefault) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: loc,\n message: \"Missing x-default hreflang in sitemap url block\",\n });\n }\n\n // Check for duplicate hreflang keys\n if (checkDuplicateKeys) {\n const seenKeys = new Set<string>();\n for (const link of links) {\n if (seenKeys.has(link.hreflang)) {\n issues.push({\n code: \"DUPLICATE_HREFLANG_KEY\",\n entryUrl: loc,\n message: `Duplicate hreflang key: ${link.hreflang}`,\n });\n }\n seenKeys.add(link.hreflang);\n }\n }\n\n // Check for duplicate hrefs (excluding x-default which can share href with another locale)\n if (checkDuplicateHrefs) {\n const hrefToLocales = new Map<string, string[]>();\n for (const link of links) {\n const locales = hrefToLocales.get(link.href) ?? [];\n locales.push(link.hreflang);\n hrefToLocales.set(link.href, locales);\n }\n for (const [href, locales] of hrefToLocales) {\n // Filter out x-default - it's allowed to share href with another locale\n const nonXDefaultLocales = locales.filter((l) => l !== \"x-default\");\n if (nonXDefaultLocales.length > 1) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: loc,\n message: `Duplicate hreflang href detected: ${href} (locales: ${nonXDefaultLocales.join(\", \")})`,\n });\n }\n }\n }\n\n // Check hreflang casing\n if (checkHreflangCasing) {\n for (const link of links) {\n if (!isValidHreflangCasing(link.hreflang)) {\n issues.push({\n code: \"INVALID_HREFLANG_CASING\",\n entryUrl: loc,\n message: `Invalid hreflang casing: ${link.hreflang}. Expected format: en, pt-BR, or x-default`,\n });\n }\n }\n }\n\n // Check origin policy\n if (originPolicy === \"same\") {\n const locOrigin = getOrigin(loc);\n if (locOrigin) {\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && linkOrigin !== locOrigin) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Inconsistent origin for ${link.hreflang}: expected ${locOrigin}, got ${linkOrigin}`,\n });\n }\n }\n }\n } else if (originPolicy === \"allowlist\") {\n const allowedOrigins = options.allowedOrigins ?? [];\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && !allowedOrigins.includes(linkOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for ${link.hreflang}: ${linkOrigin}`,\n });\n }\n }\n const locOrigin = getOrigin(loc);\n if (locOrigin && !allowedOrigins.includes(locOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for loc: ${locOrigin}`,\n });\n }\n }\n\n if (requireAbsolute) {\n for (const link of links) {\n if (!isAbsolute(link.href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute hreflang href for ${link.hreflang}: ${link.href}`,\n });\n }\n }\n if (!isAbsolute(loc)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute <loc>: ${loc}`,\n });\n }\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n\nfunction getOrigin(url: string): string | null {\n try {\n const u = new URL(url);\n return u.origin;\n } catch {\n return null;\n }\n}\n\nfunction isValidHreflangCasing(key: string): boolean {\n if (key === \"x-default\") return true;\n // en, de, uk - only lowercase\n if (/^[a-z]{2}$/.test(key)) return true;\n // pt-BR, en-US - lowercase-UPPERCASE\n if (/^[a-z]{2}-[A-Z]{2}$/.test(key)) return true;\n return false;\n}\n"],"mappings":";AASO,SAAS,eACd,SACA,SACgB;AAChB,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,8BAA8B,QAAQ,+BAA+B;AAC3E,QAAM,cAAc,QAAQ,eAAe;AAE3C,QAAM,SAA0B,CAAC;AAEjC,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAY,MAAM,YAAY;AACpC,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAQ,OAAO,QAAQ,SAAS;AAEtC,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,QAChB,SAAS;AAAA,MACX,CAAC;AACD;AAAA,IACF;AAEA,QAAI,+BAA+B,MAAM,SAAS,KAAK,CAAC,UAAU,WAAW,GAAG;AAC9E,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,QAChB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,QAAI,eAAe,QAAQ,iBAAiB;AAC1C,UAAI,CAAC,UAAU,QAAQ,eAAe,GAAG;AACvC,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,SAAS,6CAA6C,QAAQ,eAAe;AAAA,QAC/E,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,WAAW,oBAAI,IAAY;AACjC,eAAW,CAAC,QAAQ,IAAI,KAAK,OAAO;AAClC,UAAI,CAAC,iBAAiB,MAAM,GAAG;AAC7B,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,SAAS,uBAAuB,MAAM;AAAA,QACxC,CAAC;AAAA,MACH;AACA,UAAI,mBAAmB,CAAC,cAAc,IAAI,GAAG;AAC3C,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,SAAS,kCAAkC,MAAM,KAAK,IAAI;AAAA,QAC5D,CAAC;AAAA,MACH;AACA,UAAI,SAAS,IAAI,IAAI,GAAG;AACtB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU,MAAM;AAAA,UAChB,SAAS,qCAAqC,IAAI;AAAA,QACpD,CAAC;AAAA,MACH;AACA,eAAS,IAAI,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,OAAO,WAAW,GAAG,OAAO;AAC3C;AAEA,SAAS,cAAc,KAAsB;AAC3C,SAAO,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU;AAC/D;AAEA,SAAS,iBAAiB,KAAsB;AAC9C,MAAI,QAAQ,YAAa,QAAO;AAChC,SAAO,yBAAyB,KAAK,GAAG;AAC1C;;;ACvFO,SAAS,aAAa,KAAa,eAA4C;AACpF,QAAM,IAAI,IAAI,IAAI,GAAG;AACrB,MAAI,kBAAkB,WAAY,QAAO,EAAE,SAAS;AACpD,MAAI,kBAAkB,UAAU;AAC9B,QAAI,CAAC,EAAE,SAAS,SAAS,GAAG,EAAG,GAAE,WAAW,GAAG,EAAE,QAAQ;AACzD,WAAO,EAAE,SAAS;AAAA,EACpB;AACA,MAAI,EAAE,aAAa,OAAO,EAAE,SAAS,SAAS,GAAG,EAAG,GAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACvF,SAAO,EAAE,SAAS;AACpB;AAEO,SAAS,mBAAmB,OAAe,SAAyB;AACzE,MAAI,MAAM,WAAW,SAAS,KAAK,MAAM,WAAW,UAAU,EAAG,QAAO;AACxE,SAAO,IAAI,IAAI,MAAM,WAAW,GAAG,IAAI,QAAQ,IAAI,KAAK,IAAI,OAAO,EAAE,SAAS;AAChF;AAEO,SAAS,yBAAyB,aAA6B;AACpE,QAAM,IAAI,IAAI,IAAI,WAAW;AAC7B,SAAO,GAAG,EAAE,QAAQ,KAAK,EAAE,IAAI;AACjC;;;ACHO,SAAS,kBAAkB,OAAwD;AACxF,QAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAM,YAAoC,CAAC;AAE3C,aAAW,UAAU,MAAM,SAAS;AAClC,UAAM,OAAO,MAAM,YAAY,MAAM;AACrC,UAAM,MAAM,mBAAmB,MAAM,MAAM,OAAO;AAClD,cAAU,MAAM,IAAI,aAAa,KAAK,aAAa;AAAA,EACrD;AAEA,QAAM,YAAY,UAAU,MAAM,eAAe,KAAK,aAAa,MAAM,SAAS,aAAa;AAE/F,MAAI,MAAM,iBAAiB;AACzB,UAAM,WAAW,4BAA4B;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,SAAS,MAAM;AAAA,MACf,UAAU,MAAM,oBAAoB,EAAE,MAAM,MAAM;AAAA,MAClD;AAAA,IACF,CAAC;AACD,cAAU,WAAW,IAAI;AAAA,EAC3B;AAEA,SAAO,EAAE,WAAW,UAAU;AAChC;AAEO,SAAS,4BAA4B,MAMjC;AACT,QAAM,EAAE,WAAW,WAAW,SAAS,UAAU,cAAc,IAAI;AAEnE,MAAI,SAAS,SAAS,MAAO,QAAO,aAAa,WAAW,aAAa;AACzE,MAAI,SAAS,SAAS,OAAQ,QAAO,aAAa,mBAAmB,KAAK,OAAO,GAAG,aAAa;AACjG,MAAI,SAAS,SAAS,SAAU,QAAO,aAAa,mBAAmB,SAAS,KAAK,OAAO,GAAG,aAAa;AAC5G,MAAI,SAAS,SAAS,UAAU;AAC9B,UAAMA,QAAO,UAAU,SAAS,MAAM;AACtC,QAAIA,MAAM,QAAO,aAAaA,OAAM,aAAa;AACjD,WAAO,aAAa,WAAW,aAAa;AAAA,EAC9C;AACA,QAAM,OAAO,SAAS,QAAQ,EAAE,KAAK,UAAU,CAAC;AAChD,SAAO,aAAa,mBAAmB,MAAM,OAAO,GAAG,aAAa;AACtE;;;ACvCO,SAAS,aACd,SACA,SACK;AACL,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,aAAa,QAAQ,cAAc;AAEzC,SAAO,QAAQ,IAAI,CAAC,UAAU;AAC5B,QAAI,QAAQ,eAAe,CAAC,QAAQ,YAAY,KAAK,EAAG,QAAO;AAE/D,UAAM,MAAM,kBAAkB,MAAM,KAAK,QAAQ,SAAS,gBAAgB,aAAa;AAEvF,UAAM,YAAY,MAAM,YAAY,YAChC,mBAAmB,MAAM,WAAW,WAAW,QAAQ,SAAS,gBAAgB,aAAa,IAC7F;AAEJ,QAAI,CAAC,WAAW;AACd,aAAO,EAAE,GAAG,OAAO,IAAI;AAAA,IACzB;AAEA,UAAM,gBAAgB,EAAE,GAAG,UAAU;AAErC,QAAI,cAAc,QAAQ,iBAAiB;AACzC,UAAI,CAAC,cAAc,QAAQ,eAAe,EAAG,eAAc,QAAQ,eAAe,IAAI;AAAA,IACxF;AAEA,QAAI,gBAAgB;AAClB,UAAI,CAAC,cAAc,WAAW,GAAG;AAC/B,cAAM,WAAW,4BAA4B;AAAA,UAC3C,WAAW;AAAA,UACX,WAAW;AAAA,UACX,SAAS,QAAQ,WAAW;AAAA,UAC5B,UAAU,QAAQ,oBAAoB,EAAE,MAAM,MAAM;AAAA,UACpD;AAAA,QACF,CAAC;AACD,sBAAc,WAAW,IAAI;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,GAAI,WAAW,cAAc;AAE3E,WAAO,EAAE,GAAG,OAAO,KAAK,WAAW;AAAA,EACrC,CAAC;AACH;AAEA,SAAS,kBACP,KACA,SACA,gBACA,eACQ;AACR,QAAM,MAAM,iBAAiB,mBAAmB,KAAK,WAAW,GAAG,IAAI;AACvE,SAAO,aAAa,KAAK,aAAa;AACxC;AAEA,SAAS,mBACP,WACA,SACA,gBACA,eACwB;AACxB,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC9C,UAAM,MAAM,iBAAiB,mBAAmB,GAAG,WAAW,CAAC,IAAI;AACnE,QAAI,CAAC,IAAI,aAAa,KAAK,aAAa;AAAA,EAC1C;AACA,SAAO;AACT;AASO,SAAS,qBAAqB,SAAsC,SAAgD;AACzH,SAAO,eAAmB,SAAS,OAAO;AAC5C;AAMO,SAAS,wBACd,SACA,UACA,SAQK;AACL,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,aAAa,QAAQ,cAAc;AAEzC,SAAO,QAAQ,IAAI,CAAC,UAAU;AAC5B,QAAI,QAAQ,eAAe,CAAC,QAAQ,YAAY,KAAK,EAAG,QAAO;AAG/D,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM,KAAK,QAAQ,OAAO;AAC9C,iBAAW,IAAI;AAAA,IACjB,QAAQ;AACN,iBAAW,MAAM;AAAA,IACnB;AAGA,UAAM,YAAoC,CAAC;AAC3C,eAAW,UAAU,SAAS,SAAS;AACrC,YAAM,OAAO,SAAS,QAAQ,EAAE,UAAU,OAAO,CAAC;AAClD,YAAM,MAAM,iBAAiB,mBAAmB,MAAM,QAAQ,OAAO,IAAI;AACzE,gBAAU,MAAM,IAAI,aAAa,KAAK,aAAa;AAAA,IACrD;AAGA,QAAI,kBAAkB,CAAC,UAAU,WAAW,GAAG;AAC7C,YAAM,YACJ,UAAU,SAAS,eAAe,KAAK,aAAa,MAAM,KAAK,aAAa;AAC9E,YAAM,WAAW,4BAA4B;AAAA,QAC3C;AAAA,QACA;AAAA,QACA,SAAS,QAAQ;AAAA,QACjB,UAAU,SAAS,YAAY,EAAE,MAAM,MAAM;AAAA,QAC7C;AAAA,MACF,CAAC;AACD,gBAAU,WAAW,IAAI;AAAA,IAC3B;AAGA,QAAI,cAAc,CAAC,UAAU,SAAS,eAAe,GAAG;AACtD,YAAM,MAAM,kBAAkB,MAAM,KAAK,QAAQ,SAAS,gBAAgB,aAAa;AACvF,gBAAU,SAAS,eAAe,IAAI;AAAA,IACxC;AAEA,UAAM,aAAa,EAAE,GAAI,MAAM,cAAc,CAAC,GAAI,UAAU;AAE5D,WAAO,EAAE,GAAG,OAAO,WAAW;AAAA,EAChC,CAAC;AACH;;;AC5JO,SAAS,sBACd,SACiB;AACjB,QAAM,EAAE,eAAe,SAAS,WAAW,GAAG,IAAI;AAElD,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,SAAS,CAAC,EAAE,UAAU,OAAO,MAAM;AACjC,YAAM,iBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AACzE,YAAM,OAAO,YAAY;AAEzB,UAAI,WAAW,eAAe;AAE5B,eAAO,GAAG,IAAI,GAAG,cAAc;AAAA,MACjC;AAEA,aAAO,GAAG,IAAI,IAAI,MAAM,GAAG,cAAc;AAAA,IAC3C;AAAA,EACF;AACF;AAQO,SAAS,oBACd,SACiB;AACjB,QAAM,EAAE,eAAe,SAAS,WAAW,GAAG,IAAI;AAElD,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,SAAS,CAAC,EAAE,UAAU,OAAO,MAAM;AACjC,YAAM,iBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AACzE,YAAM,OAAO,YAAY;AACzB,aAAO,GAAG,IAAI,IAAI,MAAM,GAAG,cAAc;AAAA,IAC3C;AAAA,EACF;AACF;AAQO,SAAS,mBACd,SACiB;AACjB,QAAM,EAAE,eAAe,SAAS,eAAe,IAAI;AAEnD,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,SAAS,CAAC,EAAE,UAAU,OAAO,MAAM;AACjC,YAAM,SAAS,eAAe,MAAM;AACpC,UAAI,CAAC,QAAQ;AAEX,cAAM,gBAAgB,eAAe,aAAa,KAAK;AACvD,cAAMC,kBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AACzE,eAAO,GAAG,aAAa,IAAI,MAAM,GAAGA,eAAc;AAAA,MACpD;AACA,YAAM,iBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AACzE,aAAO,GAAG,MAAM,GAAG,cAAc;AAAA,IACnC;AAAA,EACF;AACF;AAQO,SAAS,oBACd,SACiB;AACjB,QAAM,EAAE,eAAe,SAAS,WAAW,GAAG,IAAI;AAElD,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,SAAS,CAAC,EAAE,UAAU,OAAO,MAAM;AACjC,YAAM,iBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AAEzE,YAAM,OACJ,eAAe,SAAS,GAAG,KAAK,mBAAmB,MAC/C,eAAe,MAAM,GAAG,EAAE,IAC1B;AACN,YAAM,OAAO,YAAY;AAEzB,UAAI,WAAW,eAAe;AAE5B,eAAO,GAAG,IAAI,GAAG,IAAI;AAAA,MACvB;AAEA,aAAO,GAAG,IAAI,GAAG,IAAI,IAAI,MAAM;AAAA,IACjC;AAAA,EACF;AACF;AAKO,SAAS,cAAc,SAKV;AAClB,MAAI,QAAQ,aAAa,QAAW;AAClC,WAAO;AAAA,MACL,SAAS,QAAQ;AAAA,MACjB,iBAAiB,QAAQ;AAAA,MACzB,SAAS,QAAQ;AAAA,MACjB,UAAU,QAAQ;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS,QAAQ;AAAA,IACjB,iBAAiB,QAAQ;AAAA,IACzB,SAAS,QAAQ;AAAA,EACnB;AACF;AAaO,SAAS,YAAY,SAA8C;AACxE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,WAAW,CAAC,SAAS,aAAa,aAAa,QAAQ;AAAA,IACvD,oBAAoB;AAAA,EACtB,IAAI;AAEJ,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB;AAAA,IACjB,SAAS,CAAC,EAAE,UAAU,OAAO,MAAM;AAEjC,YAAM,iBAAiB,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ;AAGzE,UAAI,mBAAmB,OAAO,mBAAmB,IAAI;AACnD,YAAI,WAAW,cAAe,QAAO;AACrC,eAAO,IAAI,MAAM;AAAA,MACnB;AAIA,UAAI,kBAAkB,KAAK,cAAc,GAAG;AAC1C,cAAM,QAAQ,eAAe,MAAM,GAAG,EAAE,OAAO,OAAO;AACtD,YAAI,MAAM,UAAU,GAAG;AAErB,gBAAM,UAAU,MAAM,CAAC;AACvB,gBAAM,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AACpC,iBAAO,IAAI,OAAO,IAAI,MAAM,IAAI,IAAI;AAAA,QACtC;AAAA,MACF;AAIA,YAAM,cAAc,SAAS,KAAK,CAAC,QAAQ;AACzC,cAAM,gBAAgB,IAAI,WAAW,GAAG,IAAI,MAAM,IAAI,GAAG;AACzD,eAAO,mBAAmB,iBAAiB,mBAAmB,GAAG,aAAa;AAAA,MAChF,CAAC;AACD,UAAI,aAAa;AACf,cAAM,gBAAgB,YAAY,WAAW,GAAG,IAC5C,YAAY,QAAQ,OAAO,EAAE,IAC7B,IAAI,YAAY,QAAQ,OAAO,EAAE,CAAC;AACtC,YAAI,WAAW,cAAe,QAAO;AACrC,eAAO,GAAG,aAAa,IAAI,MAAM;AAAA,MACnC;AAGA,UAAI,WAAW,cAAe,QAAO;AACrC,aAAO,IAAI,MAAM,GAAG,cAAc;AAAA,IACpC;AAAA,EACF;AACF;;;ACvMO,SAAS,oBACd,SACA,SACK;AACL,MAAI,QAAQ,SAAS,gBAAgB;AACnC,WAAO,CAAC,GAAG,OAAO;AAAA,EACpB;AAEA,QAAM,SAAc,CAAC;AAErB,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAY,MAAM,YAAY;AACpC,QAAI,CAAC,aAAa,OAAO,KAAK,SAAS,EAAE,WAAW,GAAG;AACrD,aAAO,KAAK,KAAK;AACjB;AAAA,IACF;AAGA,eAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,SAAS,GAAG;AACtD,UAAI,WAAW,eAAe,CAAC,QAAQ,gBAAiB;AAGxD,UAAI,QAAQ,WAAW,CAAC,QAAQ,QAAQ,SAAS,MAAM,EAAG;AAE1D,YAAM,gBAAmB;AAAA,QACvB,GAAG;AAAA,QACH,KAAK;AAAA;AAAA,MAEP;AACA,aAAO,KAAK,aAAa;AAAA,IAC3B;AAAA,EACF;AAGA,QAAM,OAAO,oBAAI,IAAY;AAC7B,SAAO,OAAO,OAAO,CAAC,MAAM;AAC1B,QAAI,KAAK,IAAI,EAAE,GAAG,EAAG,QAAO;AAC5B,SAAK,IAAI,EAAE,GAAG;AACd,WAAO;AAAA,EACT,CAAC;AACH;;;ACPO,SAAS,iCACd,OACA,SACgB;AAChB,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,kBAAkB,QAAQ,kBAAkB;AAClD,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,iBAAiB,QAAQ,iBAAiB,IAAI,IAAI,QAAQ,cAAc,IAAI;AAElF,QAAM,UAA0B,CAAC;AAEjC,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,cAAc,KAAK,IAAI;AACpC,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,OAAO,KAAK,OAAO,EAAE,OAAO,CAAC,WAAY,iBAAiB,eAAe,IAAI,MAAM,IAAI,IAAK;AAC5G,QAAI,QAAQ,WAAW,EAAG;AAE1B,UAAM,sBAAsB,QAAQ,mBAAmB,QAAQ;AAC/D,UAAM,2BACJ,QAAQ,SAAS,mBAAmB,IAAI,sBAAsB,QAAQ,CAAC,KAAK;AAE9E,UAAM,YAAoC,CAAC;AAC3C,eAAW,UAAU,SAAS;AAC5B,YAAM,WACJ,QAAQ,cAAc;AAAA,QACpB;AAAA,QACA;AAAA,QACA,eAAe,QAAQ;AAAA,QACvB,aAAa,QAAQ;AAAA,QACrB;AAAA,MACF,CAAC,KACD,cAAc;AAAA,QACZ;AAAA,QACA;AAAA,QACA,eAAe,QAAQ;AAAA,QACvB,aAAa,QAAQ;AAAA,QACrB;AAAA,MACF,CAAC;AAEH,YAAM,OAAO,aAAa,mBAAmB,UAAU,QAAQ,OAAO,GAAG,aAAa;AACtF,gBAAU,MAAM,IAAI;AAAA,IACtB;AAEA,UAAM,eACJ,UAAU,wBAAwB,KAClC,aAAa,mBAAmB,KAAK,QAAQ,OAAO,GAAG,aAAa;AAEtE,UAAM,eAAe,iBAAiB,IAAI;AAE1C,UAAM,YAA0B;AAAA,MAC9B,KAAK;AAAA,MACL,YAAY,EAAE,UAAU;AAAA,MACxB,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,MACvC,GAAI,KAAK,kBAAkB,EAAE,iBAAiB,KAAK,gBAAgB,IAAI,CAAC;AAAA,MACxE,GAAI,OAAO,KAAK,aAAa,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,MACvE,GAAI,KAAK,SAAS,EAAE,QAAQ,CAAC,GAAG,KAAK,MAAM,EAAE,IAAI,CAAC;AAAA,IACpD;AAEA,UAAM,CAAC,iBAAiB,IAAI,aAAa,CAAC,SAAS,GAAG;AAAA,MACpD,SAAS,QAAQ;AAAA,MACjB;AAAA,MACA,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,GAAI,QAAQ,mBAAmB,EAAE,kBAAkB,QAAQ,iBAAiB,IAAI,CAAC;AAAA,IACnF,CAAC;AAED,QAAI,kBAAmB,SAAQ,KAAK,iBAAiB;AAAA,EACvD;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,MAMZ;AACT,QAAM,UAAU,iBAAiB,KAAK,WAAW;AAEjD,MAAI,KAAK,eAAe,oBAAoB;AAC1C,QAAI,KAAK,WAAW,KAAK,cAAe,QAAO,SAAS,SAAS,KAAK,IAAI;AAC1E,WAAO,SAAS,IAAI,KAAK,MAAM,IAAI,SAAS,KAAK,IAAI;AAAA,EACvD;AAEA,MAAI,KAAK,eAAe,iBAAiB;AACvC,WAAO,SAAS,IAAI,KAAK,MAAM,IAAI,SAAS,KAAK,IAAI;AAAA,EACvD;AAEA,MAAI,KAAK,eAAe,iBAAiB;AACvC,QAAI,KAAK,WAAW,KAAK,cAAe,QAAO,SAAS,SAAS,KAAK,IAAI;AAC1E,WAAO,SAAS,SAAS,KAAK,MAAM,KAAK,MAAM;AAAA,EACjD;AAEA,SAAO,SAAS,SAAS,KAAK,QAAQ,KAAK,IAAI;AACjD;AAEA,SAAS,cAAc,MAAsB;AAC3C,SAAO,KAAK,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;AAC3D;AAEA,SAAS,iBAAiB,aAA6B;AACrD,QAAM,aAAa,YAAY,KAAK;AACpC,MAAI,CAAC,cAAc,eAAe,IAAK,QAAO;AAC9C,SAAO,IAAI,WAAW,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,CAAC;AAC/D;AAEA,SAAS,YAAY,OAAyB;AAC5C,QAAM,UAAU,MACb,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO,EACd,IAAI,CAAC,SAAS,KAAK,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,CAAC;AAE7D,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,IAAI,QAAQ,KAAK,GAAG,CAAC;AAC9B;AAEA,SAAS,OAAO,QAAqC;AACnD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC;AAC5B;AAEA,SAAS,iBAAiB,MAAwD;AAChF,SAAO,KAAK,gBAAgB,KAAK,aAAa,KAAK,eAAe,KAAK;AACzE;;;ACxKO,SAAS,UAAU,OAAuB;AAC/C,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;AAEO,SAAS,kBAAkB,KAAsB;AACtD,SAAO,uEAAuE,KAAK,GAAG;AACxF;AAEO,SAAS,qBAAqB,KAAqB;AACxD,MAAI,kBAAkB,GAAG,EAAG,QAAO;AACnC,SAAO,IAAI;AAAA,IACT;AAAA,IACA,CAAC,MAAO,EAAE,SAAS,cAAc,IAAI,IAAI,EAAE,QAAQ,WAAW,oDAAoD;AAAA,EACpH;AACF;AAEO,SAAS,iBAAiB,KAAuB;AACtD,SAAO,IAAI,MAAM,uBAAuB,KAAK,CAAC;AAChD;AAEO,SAAS,WAAW,UAAiC;AAC1D,SAAO,SAAS,MAAM,qBAAqB,IAAI,CAAC,GAAG,KAAK,KAAK;AAC/D;AAEO,SAAS,kBAAkB,UAA6D;AAC7F,QAAM,MAAiD,CAAC;AACxD,QAAM,KAAK;AACX,aAAW,KAAK,SAAS,SAAS,EAAE,GAAG;AACrC,QAAI,EAAE,CAAC,MAAM,UAAa,EAAE,CAAC,MAAM,QAAW;AAC5C,UAAI,KAAK,EAAE,UAAU,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,YAAY,UAA2B;AACrD,SAAO,iDAAiD,KAAK,QAAQ;AACvE;AAEO,SAAS,gBAAgB,UAAkB,SAAyB;AACzE,QAAM,cAAc,SAAS,YAAY,aAAa;AACtD,MAAI,gBAAgB,IAAI;AACtB,UAAM,SAAS,SAAS,QAAQ,QAAQ;AACxC,QAAI,WAAW,GAAI,QAAO;AAC1B,UAAM,YAAY,SAAS,SAAS;AACpC,WAAO,GAAG,SAAS,MAAM,GAAG,SAAS,CAAC;AAAA,MAAS,OAAO,GAAG,SAAS,MAAM,SAAS,CAAC;AAAA,EACpF;AACA,QAAM,UAAU,SAAS,QAAQ,MAAM,WAAW;AAClD,MAAI,YAAY,GAAI,QAAO,GAAG,QAAQ;AAAA,MAAS,OAAO;AACtD,SAAO,GAAG,SAAS,MAAM,GAAG,OAAO,CAAC;AAAA,MAAS,OAAO,GAAG,SAAS,MAAM,OAAO,CAAC;AAChF;AAOO,SAAS,kBAAkB,UAAkB,SAAiC;AACnF,MAAI,QAAQ,UAAU,WAAY,QAAO;AAEzC,QAAM,QAAQ,kBAAkB,QAAQ;AACxC,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,QAAM,MAAM,WAAW,QAAQ;AAG/B,MAAI;AACJ,MAAI,QAAQ,iBAAiB;AAC3B,gBAAY,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ,eAAe;AAAA,EACtE;AACA,MAAI,CAAC,aAAa,KAAK;AACrB,gBAAY,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG;AAAA,EAC9C;AAGA,QAAM,iBAAiB,YAAY,CAAC,SAAS,IAAI,CAAC;AAClD,QAAM,aAAa,MAAM,OAAO,CAAC,MAAM,MAAM,aAAa,EAAE,aAAa,WAAW;AACpF,QAAM,gBAAgB,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,WAAW;AAGpE,QAAM,eAAe,CAAC,GAAG,gBAAgB,GAAG,YAAY,GAAG,aAAa;AAGxE,QAAM,WAAW,SAAS,QAAQ,2BAA2B,EAAE;AAG/D,QAAM,SAAS,SAAS,QAAQ,QAAQ;AACxC,MAAI,WAAW,GAAI,QAAO;AAE1B,QAAM,YAAY,SAAS,SAAS;AACpC,QAAM,WAAW,aACd,IAAI,CAAC,MAAM;AAAA,4CAA+C,UAAU,EAAE,QAAQ,CAAC,WAAW,UAAU,EAAE,IAAI,CAAC,MAAM,EACjH,KAAK,EAAE;AAEV,SAAO,GAAG,SAAS,MAAM,GAAG,SAAS,CAAC,GAAG,QAAQ;AAAA,IAAO,SAAS,MAAM,SAAS,EAAE,UAAU,CAAC;AAC/F;AAEO,SAAS,8BACd,UACA,QACQ;AACR,MAAI,WAAW,WAAY,QAAO;AAGlC,MAAI,SAAS,SAAS,QAAQ,wBAAwB,CAAC,GAAG,QAAgB;AACxE,WAAO,QAAQ,yBAAyB,KAAK,MAAM,CAAC;AAAA,EACtD,CAAC;AAGD,WAAS,OAAO;AAAA,IACd;AAAA,IACA,CAAC,GAAG,QAAgB,KAAa,WAAmB;AAClD,aAAO,GAAG,MAAM,GAAG,yBAAyB,KAAK,MAAM,CAAC,GAAG,MAAM;AAAA,IACnE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,yBAAyB,KAAa,QAAoC;AACjF,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,QAAI,WAAW,YAAY,CAAC,EAAE,SAAS,SAAS,GAAG,GAAG;AACpD,QAAE,YAAY;AAAA,IAChB,WAAW,WAAW,WAAW,EAAE,aAAa,OAAO,EAAE,SAAS,SAAS,GAAG,GAAG;AAC/E,QAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AAAA,IACrC;AACA,WAAO,EAAE,SAAS;AAAA,EACpB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACjHO,SAAS,6BAA6B,KAAa,SAAgC;AACxF,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,mBAAmB,QAAQ,oBAAoB,EAAE,MAAM,MAAM;AACnE,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,gBAAgB,QAAQ,iBAAiB;AAE/C,QAAM,YAAY,kBAAkB,qBAAqB,GAAG,IAAI;AAEhE,QAAM,SAAS,iBAAiB,SAAS;AACzC,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,MAAI,MAAM;AAEV,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,WAAW,KAAK;AAC5B,QAAI,CAAC,IAAK;AAEV,UAAM,QAAQ,kBAAkB,KAAK;AACrC,QAAI,MAAM,WAAW,EAAG;AAExB,QAAI,YAAY;AAEhB,QAAI,CAAC,YAAY,KAAK,GAAG;AACvB,YAAM,OAAO,oBAAoB;AAAA,QAC/B;AAAA,QACA;AAAA,QACA,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,QACtD,UAAU;AAAA,MACZ,CAAC;AAED,YAAM,UAAU,0DAA0D,UAAU,IAAI,CAAC;AACzF,kBAAY,gBAAgB,WAAW,OAAO;AAAA,IAChD;AAGA,QAAI,UAAU,mBAAmB;AAC/B,kBAAY,kBAAkB,WAAW;AAAA,QACvC,GAAI,QAAQ,kBAAkB,EAAE,iBAAiB,QAAQ,gBAAgB,IAAI,CAAC;AAAA,QAC9E,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAGA,QAAI,kBAAkB,YAAY;AAChC,kBAAY,8BAA8B,WAAW,aAAa;AAAA,IACpE;AAEA,UAAM,IAAI,QAAQ,OAAO,SAAS;AAAA,EACpC;AAEA,SAAO;AACT;AAEA,SAAS,oBAAoB,MAKlB;AACT,QAAM,EAAE,KAAK,OAAO,SAAS,SAAS,IAAI;AAE1C,QAAM,SAAS,UAAU,mBAAmB,KAAK,OAAO,IAAI;AAC5D,QAAM,SAAS,WAAW,MAAM,IAAI,yBAAyB,MAAM,IAAI;AAEvE,MAAI,SAAS,SAAS,MAAO,QAAO;AACpC,MAAI,SAAS,SAAS,QAAQ;AAC5B,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,mBAAmB,KAAK,MAAM;AAAA,EACvC;AACA,MAAI,SAAS,SAAS,UAAU;AAC9B,QAAI,CAAC,OAAQ,QAAO,SAAS;AAC7B,WAAO,mBAAmB,SAAS,KAAK,MAAM;AAAA,EAChD;AACA,MAAI,SAAS,SAAS,UAAU;AAC9B,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,SAAS,MAAM,GAAG;AACjE,QAAI,MAAO,QAAO,UAAU,mBAAmB,OAAO,OAAO,IAAI;AACjE,WAAO;AAAA,EACT;AACA,QAAM,WAAW,SAAS,QAAQ,EAAE,KAAK,OAAO,CAAC;AACjD,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,mBAAmB,UAAU,MAAM;AAC5C;AAEA,SAAS,WAAW,GAAoB;AACtC,SAAO,EAAE,WAAW,SAAS,KAAK,EAAE,WAAW,UAAU;AAC3D;;;AC9FO,SAAS,wBAAwB,KAAa,SAA0C;AAC7F,QAAM,mBAAmB,QAAQ,oBAAoB;AACrD,QAAM,8BAA8B,QAAQ,+BAA+B;AAC3E,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,qBAAqB,QAAQ,sBAAsB;AACzD,QAAM,sBAAsB,QAAQ,uBAAuB;AAC3D,QAAM,sBAAsB,QAAQ,uBAAuB;AAC3D,QAAM,eAAe,QAAQ,gBAAgB;AAE7C,QAAM,SAA0B,CAAC;AAEjC,QAAM,SAAS,iBAAiB,GAAG;AAEnC,MAAI,kBAAkB;AACpB,UAAM,YAAY,gBAAgB,KAAK,GAAG;AAC1C,QAAI,aAAa,CAAC,kBAAkB,GAAG,GAAG;AACxC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,WAAW,KAAK;AAC5B,QAAI,CAAC,IAAK;AAEV,UAAM,QAAQ,kBAAkB,KAAK;AACrC,QAAI,MAAM,WAAW,EAAG;AAExB,UAAMC,eAAc,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,WAAW;AAChE,QAAI,+BAA+B,MAAM,SAAS,KAAK,CAACA,cAAa;AACnE,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAGA,QAAI,oBAAoB;AACtB,YAAM,WAAW,oBAAI,IAAY;AACjC,iBAAW,QAAQ,OAAO;AACxB,YAAI,SAAS,IAAI,KAAK,QAAQ,GAAG;AAC/B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,2BAA2B,KAAK,QAAQ;AAAA,UACnD,CAAC;AAAA,QACH;AACA,iBAAS,IAAI,KAAK,QAAQ;AAAA,MAC5B;AAAA,IACF;AAGA,QAAI,qBAAqB;AACvB,YAAM,gBAAgB,oBAAI,IAAsB;AAChD,iBAAW,QAAQ,OAAO;AACxB,cAAM,UAAU,cAAc,IAAI,KAAK,IAAI,KAAK,CAAC;AACjD,gBAAQ,KAAK,KAAK,QAAQ;AAC1B,sBAAc,IAAI,KAAK,MAAM,OAAO;AAAA,MACtC;AACA,iBAAW,CAAC,MAAM,OAAO,KAAK,eAAe;AAE3C,cAAM,qBAAqB,QAAQ,OAAO,CAAC,MAAM,MAAM,WAAW;AAClE,YAAI,mBAAmB,SAAS,GAAG;AACjC,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,qCAAqC,IAAI,cAAc,mBAAmB,KAAK,IAAI,CAAC;AAAA,UAC/F,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,QAAI,qBAAqB;AACvB,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,sBAAsB,KAAK,QAAQ,GAAG;AACzC,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,4BAA4B,KAAK,QAAQ;AAAA,UACpD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,QAAI,iBAAiB,QAAQ;AAC3B,YAAM,YAAY,UAAU,GAAG;AAC/B,UAAI,WAAW;AACb,mBAAW,QAAQ,OAAO;AACxB,gBAAM,aAAa,UAAU,KAAK,IAAI;AACtC,cAAI,cAAc,eAAe,WAAW;AAC1C,mBAAO,KAAK;AAAA,cACV,MAAM;AAAA,cACN,UAAU;AAAA,cACV,SAAS,2BAA2B,KAAK,QAAQ,cAAc,SAAS,SAAS,UAAU;AAAA,YAC7F,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,iBAAiB,aAAa;AACvC,YAAM,iBAAiB,QAAQ,kBAAkB,CAAC;AAClD,iBAAW,QAAQ,OAAO;AACxB,cAAM,aAAa,UAAU,KAAK,IAAI;AACtC,YAAI,cAAc,CAAC,eAAe,SAAS,UAAU,GAAG;AACtD,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,+BAA+B,KAAK,QAAQ,KAAK,UAAU;AAAA,UACtE,CAAC;AAAA,QACH;AAAA,MACF;AACA,YAAM,YAAY,UAAU,GAAG;AAC/B,UAAI,aAAa,CAAC,eAAe,SAAS,SAAS,GAAG;AACpD,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS,oCAAoC,SAAS;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,iBAAiB;AACnB,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAACC,YAAW,KAAK,IAAI,GAAG;AAC1B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,UAAU;AAAA,YACV,SAAS,kCAAkC,KAAK,QAAQ,KAAK,KAAK,IAAI;AAAA,UACxE,CAAC;AAAA,QACH;AAAA,MACF;AACA,UAAI,CAACA,YAAW,GAAG,GAAG;AACpB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,UACV,SAAS,uBAAuB,GAAG;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,OAAO,WAAW,GAAG,OAAO;AAC3C;AAEA,SAASA,YAAW,GAAoB;AACtC,SAAO,EAAE,WAAW,SAAS,KAAK,EAAE,WAAW,UAAU;AAC3D;AAEA,SAAS,UAAU,KAA4B;AAC7C,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,WAAO,EAAE;AAAA,EACX,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,sBAAsB,KAAsB;AACnD,MAAI,QAAQ,YAAa,QAAO;AAEhC,MAAI,aAAa,KAAK,GAAG,EAAG,QAAO;AAEnC,MAAI,sBAAsB,KAAK,GAAG,EAAG,QAAO;AAC5C,SAAO;AACT;","names":["href","normalizedPath","hasXDefault","isAbsolute"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/assertHreflang.ts","../src/lib/url.ts","../src/lib/buildLanguagesMap.ts","../src/lib/withHreflang.ts","../src/lib/routing.ts","../src/lib/expandLocales.ts","../src/lib/fromManifest.ts","../src/xml/xml.ts","../src/xml/inject.ts","../src/xml/check.ts"],"sourcesContent":["import type { HreflangIssue, HreflangReport, SitemapEntryLike } from \"./types.js\";\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflang(\n entries: readonly SitemapEntryLike[],\n options: AssertHreflangOptions,\n): HreflangReport {\n const requireAbsolute = options.requireAbsolute ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireSelf = options.requireSelf ?? false;\n\n const issues: HreflangIssue[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages) continue;\n\n const pairs = Object.entries(languages).filter(([, href]) => Boolean(href)) as Array<[\n string,\n string,\n ]>;\n\n if (pairs.length === 0) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: entry.url,\n message: \"alternates.languages is empty\",\n });\n continue;\n }\n\n if (requireXDefaultWhenMultiple && pairs.length > 1 && !languages[\"x-default\"]) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: entry.url,\n message: \"Missing x-default hreflang for a multilingual entry\",\n });\n }\n\n if (requireSelf && options.canonicalLocale) {\n if (!languages[options.canonicalLocale]) {\n issues.push({\n code: \"MISSING_SELF\",\n entryUrl: entry.url,\n message: `Missing self hreflang for canonicalLocale=${options.canonicalLocale}`,\n });\n }\n }\n\n const hrefSeen = new Set<string>();\n for (const [locale, href] of pairs) {\n if (!isValidLocaleKey(locale)) {\n issues.push({\n code: \"INVALID_LOCALE_KEY\",\n entryUrl: entry.url,\n message: `Invalid locale key: ${locale}`,\n });\n }\n if (requireAbsolute && !isAbsoluteUrl(href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: entry.url,\n message: `Non-absolute hreflang href for ${locale}: ${href}`,\n });\n }\n if (hrefSeen.has(href)) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: entry.url,\n message: `Duplicate hreflang href detected: ${href}`,\n });\n }\n hrefSeen.add(href);\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n return url.startsWith(\"http://\") || url.startsWith(\"https://\");\n}\n\nfunction isValidLocaleKey(key: string): boolean {\n if (key === \"x-default\") return true;\n return /^[a-z]{2}(-[A-Z]{2})?$/.test(key);\n}\n","export type TrailingSlashPolicy = \"preserve\" | \"always\" | \"never\";\n\nexport function normalizeUrl(url: string, trailingSlash: TrailingSlashPolicy): string {\n const u = new URL(url);\n if (trailingSlash === \"preserve\") return u.toString();\n if (trailingSlash === \"always\") {\n if (!u.pathname.endsWith(\"/\")) u.pathname = `${u.pathname}/`;\n return u.toString();\n }\n if (u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) u.pathname = u.pathname.slice(0, -1);\n return u.toString();\n}\n\nexport function resolveAbsoluteUrl(input: string, baseUrl: string): string {\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) return input;\n return new URL(input.startsWith(\"/\") ? input : `/${input}`, baseUrl).toString();\n}\n\nexport function getOriginFromAbsoluteUrl(absoluteUrl: string): string {\n const u = new URL(absoluteUrl);\n return `${u.protocol}//${u.host}`;\n}\n","import type { XDefaultStrategy } from \"./types.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type BuildLanguagesMapInput = {\n baseUrl: string;\n locales: readonly string[];\n canonicalLocale: string;\n resolveHref: (locale: string) => string;\n includeXDefault: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport type BuildLanguagesMapOutput = {\n canonical: string;\n languages: Record<string, string>;\n};\n\nexport function buildLanguagesMap(input: BuildLanguagesMapInput): BuildLanguagesMapOutput {\n const trailingSlash = input.trailingSlash ?? \"preserve\";\n const languages: Record<string, string> = {};\n\n for (const locale of input.locales) {\n const href = input.resolveHref(locale);\n const abs = resolveAbsoluteUrl(href, input.baseUrl);\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n const canonical = languages[input.canonicalLocale] ?? normalizeUrl(input.baseUrl, trailingSlash);\n\n if (input.includeXDefault) {\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: input.baseUrl,\n strategy: input.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n return { canonical, languages };\n}\n\nexport function resolveXDefaultForLanguages(args: {\n canonical: string;\n languages: Record<string, string>;\n baseUrl: string;\n strategy: XDefaultStrategy;\n trailingSlash: \"preserve\" | \"always\" | \"never\";\n}): string {\n const { canonical, languages, baseUrl, strategy, trailingSlash } = args;\n\n if (strategy.type === \"loc\") return normalizeUrl(canonical, trailingSlash);\n if (strategy.type === \"root\") return normalizeUrl(resolveAbsoluteUrl(\"/\", baseUrl), trailingSlash);\n if (strategy.type === \"custom\") return normalizeUrl(resolveAbsoluteUrl(strategy.url, baseUrl), trailingSlash);\n if (strategy.type === \"locale\") {\n const href = languages[strategy.locale];\n if (href) return normalizeUrl(href, trailingSlash);\n return normalizeUrl(canonical, trailingSlash);\n }\n const href = strategy.resolve({ url: canonical });\n return normalizeUrl(resolveAbsoluteUrl(href, baseUrl), trailingSlash);\n}\n","import type {\n HreflangReport,\n SitemapEntryLike,\n XDefaultStrategy,\n RoutingStrategy,\n} from \"./types.js\";\nimport { assertHreflang as assertHreflangImpl } from \"./assertHreflang.js\";\nimport { buildLanguagesMap, resolveXDefaultForLanguages } from \"./buildLanguagesMap.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport { normalizeUrl, resolveAbsoluteUrl };\nexport { buildLanguagesMap };\n\nexport type WithHreflangOptions = {\n baseUrl?: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n canonicalLocale?: string;\n xDefaultStrategy?: XDefaultStrategy;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n};\n\nexport function withHreflang<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: WithHreflangOptions,\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n\n const languages = entry.alternates?.languages\n ? normalizeLanguages(entry.alternates.languages, options.baseUrl, ensureAbsolute, trailingSlash)\n : undefined;\n\n if (!languages) {\n return { ...entry, url } as T;\n }\n\n const nextLanguages = { ...languages };\n\n if (ensureSelf && options.canonicalLocale) {\n if (!nextLanguages[options.canonicalLocale]) nextLanguages[options.canonicalLocale] = url;\n }\n\n if (ensureXDefault) {\n if (!nextLanguages[\"x-default\"]) {\n const xDefault = resolveXDefaultForLanguages({\n canonical: url,\n languages: nextLanguages,\n baseUrl: options.baseUrl ?? url,\n strategy: options.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n nextLanguages[\"x-default\"] = xDefault;\n }\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages: nextLanguages };\n\n return { ...entry, url, alternates } as T;\n });\n}\n\nfunction normalizeEntryUrl(\n url: string,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): string {\n const abs = ensureAbsolute ? resolveAbsoluteUrl(url, baseUrl ?? url) : url;\n return normalizeUrl(abs, trailingSlash);\n}\n\nfunction normalizeLanguages(\n languages: Record<string, string | undefined>,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(languages)) {\n if (!v) continue;\n const abs = ensureAbsolute ? resolveAbsoluteUrl(v, baseUrl ?? v) : v;\n out[k] = normalizeUrl(abs, trailingSlash);\n }\n return out;\n}\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflangExport(entries: readonly SitemapEntryLike[], options: AssertHreflangOptions): HreflangReport {\n return assertHreflangImpl(entries, options);\n}\n\n/**\n * Build hreflang entries using a routing strategy.\n * This is the recommended way to use withHreflang with i18n routing.\n */\nexport function withHreflangFromRouting<T extends SitemapEntryLike>(\n entries: readonly T[],\n strategy: RoutingStrategy,\n options: {\n baseUrl: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n },\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n // Extract pathname from URL\n let pathname: string;\n try {\n const url = new URL(entry.url, options.baseUrl);\n pathname = url.pathname;\n } catch {\n pathname = entry.url;\n }\n\n // Build languages map from routing strategy\n const languages: Record<string, string> = {};\n for (const locale of strategy.locales) {\n const href = strategy.hrefFor({ pathname, locale });\n const abs = ensureAbsolute ? resolveAbsoluteUrl(href, options.baseUrl) : href;\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n // Add x-default if needed\n if (ensureXDefault && !languages[\"x-default\"]) {\n const canonical =\n languages[strategy.canonicalLocale] ?? normalizeUrl(entry.url, trailingSlash);\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: options.baseUrl,\n strategy: strategy.xDefault ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n // Ensure self for canonical locale\n if (ensureSelf && !languages[strategy.canonicalLocale]) {\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n languages[strategy.canonicalLocale] = url;\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages };\n\n return { ...entry, alternates } as T;\n });\n}\n","import type {\n RoutingStrategy,\n RoutingPrefixAsNeededOptions,\n RoutingPrefixAlwaysOptions,\n RoutingDomainBasedOptions,\n RoutingSuffixLocaleOptions,\n RoutingPAS7Options,\n} from \"./types.js\";\n\n/**\n * Prefix-as-needed routing: default locale has no prefix, others have /locale\n * Example: /about (en), /uk/about (uk), /de/about (de)\n *\n * Compatible with next-intl's prefix-as-needed strategy.\n */\nexport function routingPrefixAsNeeded(\n options: RoutingPrefixAsNeededOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n return `${base}${normalizedPath}`;\n }\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Prefix-always routing: all locales have /locale prefix\n * Example: /en/about, /uk/about, /de/about\n *\n * Compatible with next-intl's prefix-always strategy.\n */\nexport function routingPrefixAlways(\n options: RoutingPrefixAlwaysOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Domain-based routing: each locale has its own domain\n * Example: example.com (en), example.de (de), example.ua (uk)\n *\n * Compatible with next-intl's domain-based routing.\n */\nexport function routingDomainBased(\n options: RoutingDomainBasedOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, localeToDomain } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const domain = localeToDomain[locale];\n if (!domain) {\n const defaultDomain = localeToDomain[defaultLocale] || \"\";\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${defaultDomain}/${locale}${normalizedPath}`;\n }\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${domain}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Suffix-locale routing: locale added as suffix to path\n * Example: /blog (en), /blog/uk (uk), /blog/de (de)\n *\n * Common for blogs and content sections.\n */\nexport function routingSuffixLocale(\n options: RoutingSuffixLocaleOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const path =\n normalizedPath.endsWith(\"/\") && normalizedPath !== \"/\"\n ? normalizedPath.slice(0, -1)\n : normalizedPath;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n return `${base}${path}`;\n }\n return `${base}${path}/${locale}`;\n },\n };\n}\n\n/**\n * Custom routing with user-defined function\n */\nexport function routingCustom(options: {\n locales: readonly string[];\n canonicalLocale: string;\n hrefFor: (args: { pathname: string; locale: string }) => string;\n xDefault?: RoutingStrategy[\"xDefault\"];\n}): RoutingStrategy {\n if (options.xDefault !== undefined) {\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n xDefault: options.xDefault,\n };\n }\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n };\n}\n\n/**\n * PAS7 custom routing strategy:\n * - Home: / (en), /uk, /de\n * - Suffix pages: /blog (en), /blog/uk\n * - Detail pages: /blog/en/slug, /blog/uk/slug\n * - Prefix pages: /about (en), /uk/about\n *\n * Priority: detailPathPattern > suffixPaths > prefixPaths > fallback.\n */\nexport function routingPAS7(options: RoutingPAS7Options): RoutingStrategy {\n const {\n defaultLocale,\n locales,\n hubPaths = [\"/blog\", \"/projects\", \"/services\", \"/cases\"],\n suffixPaths,\n prefixPaths = [],\n detailPathPattern = /^\\/(blog|projects|services|cases)\\//,\n } = options;\n\n const normalizedSuffixPaths = uniquePaths(suffixPaths ?? hubPaths);\n const normalizedPrefixPaths = uniquePaths(prefixPaths);\n const localeSet = new Set(locales);\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = normalizeInputPath(pathname);\n\n if (normalizedPath === \"/\") {\n if (locale === defaultLocale) return \"/\";\n return `/${locale}`;\n }\n\n if (detailPathPattern.test(normalizedPath)) {\n const parts = normalizedPath.split(\"/\").filter(Boolean);\n if (parts.length >= 3 && localeSet.has(parts[1] ?? \"\")) {\n const section = parts[0];\n const slug = parts.slice(2).join(\"/\");\n return `/${section}/${locale}/${slug}`;\n }\n }\n\n const suffixBase = findSuffixBasePath(normalizedPath, normalizedSuffixPaths, localeSet);\n if (suffixBase) {\n if (locale === defaultLocale) return suffixBase;\n return `${suffixBase}/${locale}`;\n }\n\n const prefixBase = findPrefixBasePath(normalizedPath, normalizedPrefixPaths, localeSet);\n if (prefixBase) {\n if (locale === defaultLocale) return prefixBase;\n return `/${locale}${prefixBase}`;\n }\n\n if (locale === defaultLocale) return normalizedPath;\n return `/${locale}${normalizedPath}`;\n },\n };\n}\n\nfunction normalizeInputPath(pathname: string): string {\n const withLeadingSlash = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n if (withLeadingSlash.length > 1 && withLeadingSlash.endsWith(\"/\")) {\n return withLeadingSlash.slice(0, -1);\n }\n return withLeadingSlash;\n}\n\nfunction uniquePaths(paths: readonly string[]): string[] {\n return [...new Set(paths.map(normalizeInputPath))];\n}\n\nfunction findSuffixBasePath(\n pathname: string,\n suffixPaths: readonly string[],\n localeSet: ReadonlySet<string>,\n): string | null {\n for (const suffixPath of suffixPaths) {\n if (pathname === suffixPath) return suffixPath;\n\n if (!pathname.startsWith(`${suffixPath}/`)) continue;\n const rest = pathname.slice(`${suffixPath}/`.length);\n if (!rest) continue;\n\n const [maybeLocale, ...tail] = rest.split(\"/\");\n if (tail.length === 0 && localeSet.has(maybeLocale ?? \"\")) {\n return suffixPath;\n }\n }\n return null;\n}\n\nfunction findPrefixBasePath(\n pathname: string,\n prefixPaths: readonly string[],\n localeSet: ReadonlySet<string>,\n): string | null {\n for (const prefixPath of prefixPaths) {\n if (pathname === prefixPath) return prefixPath;\n\n const withoutLeadingSlash = pathname.replace(/^\\/+/, \"\");\n const [maybeLocale, ...tail] = withoutLeadingSlash.split(\"/\");\n if (!localeSet.has(maybeLocale ?? \"\")) continue;\n\n const candidate = normalizeInputPath(tail.join(\"/\"));\n if (candidate === prefixPath) return prefixPath;\n }\n return null;\n}\n","import type { SitemapEntryLike } from \"./types.js\";\n\nexport type ExpandLocalesOptions = {\n mode: \"cluster-only\" | \"expanded\";\n includeXDefault?: boolean;\n locales?: readonly string[];\n};\n\nexport function expandLocaleEntries<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: ExpandLocalesOptions,\n): T[] {\n if (options.mode === \"cluster-only\") {\n return [...entries];\n }\n\n const result: T[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages || Object.keys(languages).length === 0) {\n result.push(entry);\n continue;\n }\n\n // Create a separate entry for each locale\n for (const [locale, href] of Object.entries(languages)) {\n if (locale === \"x-default\" && !options.includeXDefault) continue;\n\n // Filter by specific locales if provided\n if (options.locales && !options.locales.includes(locale)) continue;\n\n const expandedEntry: T = {\n ...entry,\n url: href,\n // alternates.languages remains the full cluster\n };\n result.push(expandedEntry);\n }\n }\n\n // Dedupe by url\n const seen = new Set<string>();\n return result.filter((e) => {\n if (seen.has(e.url)) return false;\n seen.add(e.url);\n return true;\n });\n}\n","import type { ChangeFrequency, SitemapEntry, XDefaultStrategy } from \"./types.js\";\nimport { withHreflang } from \"./withHreflang.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type ManifestRouteStyle =\n | \"prefix-as-needed\"\n | \"prefix-always\"\n | \"suffix-locale\"\n | \"locale-segment\";\n\nexport type LocalizedManifestItem = {\n slug: string;\n locales: readonly string[];\n updatedAt?: string | Date;\n publishedAt?: string | Date;\n lastModified?: string | Date;\n date?: string | Date;\n changeFrequency?: ChangeFrequency;\n priority?: number;\n images?: string[];\n};\n\nexport type CreateSitemapEntriesFromManifestOptions = {\n baseUrl: string;\n sectionPath: string;\n defaultLocale: string;\n canonicalLocale?: string;\n routeStyle?: ManifestRouteStyle;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureXDefault?: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n includeLocales?: readonly string[];\n pathnameFor?: (args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n }) => string;\n};\n\nexport function createSitemapEntriesFromManifest<T extends LocalizedManifestItem>(\n items: readonly T[],\n options: CreateSitemapEntriesFromManifestOptions,\n): SitemapEntry[] {\n const routeStyle = options.routeStyle ?? \"locale-segment\";\n const includeXDefault = options.ensureXDefault ?? true;\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const includeLocales = options.includeLocales ? new Set(options.includeLocales) : null;\n\n const entries: SitemapEntry[] = [];\n\n for (const item of items) {\n const slug = normalizeSlug(item.slug);\n if (!slug) continue;\n\n const locales = unique(item.locales).filter((locale) => (includeLocales ? includeLocales.has(locale) : true));\n if (locales.length === 0) continue;\n\n const configuredCanonical = options.canonicalLocale ?? options.defaultLocale;\n const effectiveCanonicalLocale =\n locales.includes(configuredCanonical) ? configuredCanonical : locales[0] ?? configuredCanonical;\n\n const languages: Record<string, string> = {};\n for (const locale of locales) {\n const pathname =\n options.pathnameFor?.({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n }) ??\n buildPathname({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n });\n\n const href = normalizeUrl(resolveAbsoluteUrl(pathname, options.baseUrl), trailingSlash);\n languages[locale] = href;\n }\n\n const canonicalUrl =\n languages[effectiveCanonicalLocale] ??\n normalizeUrl(resolveAbsoluteUrl(\"/\", options.baseUrl), trailingSlash);\n\n const lastModified = pickLastModified(item);\n\n const baseEntry: SitemapEntry = {\n url: canonicalUrl,\n alternates: { languages },\n ...(lastModified ? { lastModified } : {}),\n ...(item.changeFrequency ? { changeFrequency: item.changeFrequency } : {}),\n ...(typeof item.priority === \"number\" ? { priority: item.priority } : {}),\n ...(item.images ? { images: [...item.images] } : {}),\n };\n\n const [entryWithHreflang] = withHreflang([baseEntry], {\n baseUrl: options.baseUrl,\n trailingSlash,\n ensureAbsolute: true,\n ensureXDefault: includeXDefault,\n ensureSelf: true,\n canonicalLocale: effectiveCanonicalLocale,\n ...(options.xDefaultStrategy ? { xDefaultStrategy: options.xDefaultStrategy } : {}),\n });\n\n if (entryWithHreflang) entries.push(entryWithHreflang);\n }\n\n return entries;\n}\n\nfunction buildPathname(args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n}): string {\n const section = normalizeSection(args.sectionPath);\n\n if (args.routeStyle === \"prefix-as-needed\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"prefix-always\") {\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"suffix-locale\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(section, args.slug, args.locale);\n }\n\n return joinPath(section, args.locale, args.slug);\n}\n\nfunction normalizeSlug(slug: string): string {\n return slug.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizeSection(sectionPath: string): string {\n const normalized = sectionPath.trim();\n if (!normalized || normalized === \"/\") return \"\";\n return `/${normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\nfunction joinPath(...parts: string[]): string {\n const cleaned = parts\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => part.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\"));\n\n if (cleaned.length === 0) return \"/\";\n return `/${cleaned.join(\"/\")}`;\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction pickLastModified(item: LocalizedManifestItem): string | Date | undefined {\n return item.lastModified ?? item.updatedAt ?? item.publishedAt ?? item.date;\n}\n","export function xmlEscape(input: string): string {\n return input\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nexport function hasXhtmlNamespace(xml: string): boolean {\n return /<urlset[^>]*\\sxmlns:xhtml=\"http:\\/\\/www\\.w3\\.org\\/1999\\/xhtml\"[^>]*>/.test(xml);\n}\n\nexport function ensureXhtmlNamespace(xml: string): string {\n if (hasXhtmlNamespace(xml)) return xml;\n return xml.replace(\n /<urlset(\\s[^>]*?)?>/m,\n (m) => (m.includes(\"xmlns:xhtml=\") ? m : m.replace(\"<urlset\", '<urlset xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"')),\n );\n}\n\nexport function extractUrlBlocks(xml: string): string[] {\n return xml.match(/<url>[\\s\\S]*?<\\/url>/g) ?? [];\n}\n\nexport function extractLoc(urlBlock: string): string | null {\n return urlBlock.match(/<loc>([^<]+)<\\/loc>/)?.[1]?.trim() ?? null;\n}\n\nexport function extractXhtmlLinks(urlBlock: string): Array<{ hreflang: string; href: string }> {\n const out: Array<{ hreflang: string; href: string }> = [];\n const re = /<xhtml:link[^>]*\\shreflang=\"([^\"]+)\"[^>]*\\shref=\"([^\"]+)\"[^>]*\\/>/g;\n for (const m of urlBlock.matchAll(re)) {\n if (m[1] !== undefined && m[2] !== undefined) {\n out.push({ hreflang: m[1], href: m[2] });\n }\n }\n return out;\n}\n\nexport function hasXDefault(urlBlock: string): boolean {\n return /<xhtml:link[^>]*\\shreflang=\"x-default\"[^>]*\\/>/.test(urlBlock);\n}\n\nexport function insertXhtmlLink(urlBlock: string, linkXml: string): string {\n const lastLinkIdx = urlBlock.lastIndexOf(\"<xhtml:link\");\n if (lastLinkIdx === -1) {\n const locEnd = urlBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n const insertPos = locEnd + \"</loc>\".length;\n return `${urlBlock.slice(0, insertPos)}\\n ${linkXml}${urlBlock.slice(insertPos)}`;\n }\n const lineEnd = urlBlock.indexOf(\"\\n\", lastLinkIdx);\n if (lineEnd === -1) return `${urlBlock}\\n ${linkXml}`;\n return `${urlBlock.slice(0, lineEnd)}\\n ${linkXml}${urlBlock.slice(lineEnd)}`;\n}\n\nexport type ReorderOptions = {\n canonicalLocale?: string;\n order: \"canonical-first\" | \"preserve\";\n};\n\nexport function reorderXhtmlLinks(urlBlock: string, options: ReorderOptions): string {\n if (options.order === \"preserve\") return urlBlock;\n\n const links = extractXhtmlLinks(urlBlock);\n if (links.length === 0) return urlBlock;\n\n const loc = extractLoc(urlBlock);\n\n // Determine canonical\n let canonical: { hreflang: string; href: string } | undefined;\n if (options.canonicalLocale) {\n canonical = links.find((l) => l.hreflang === options.canonicalLocale);\n }\n if (!canonical && loc) {\n canonical = links.find((l) => l.href === loc);\n }\n\n // Split into groups\n const canonicalLinks = canonical ? [canonical] : [];\n const otherLinks = links.filter((l) => l !== canonical && l.hreflang !== \"x-default\");\n const xDefaultLinks = links.filter((l) => l.hreflang === \"x-default\");\n\n // Build new order\n const orderedLinks = [...canonicalLinks, ...otherLinks, ...xDefaultLinks];\n\n // Remove all existing xhtml:link and insert in new order\n const newBlock = urlBlock.replace(/<xhtml:link[^>]*\\/>\\s*/g, \"\");\n\n // Find position after </loc>\n const locEnd = newBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n\n const insertPos = locEnd + \"</loc>\".length;\n const linksXml = orderedLinks\n .map((l) => `\\n <xhtml:link rel=\"alternate\" hreflang=\"${xmlEscape(l.hreflang)}\" href=\"${xmlEscape(l.href)}\" />`)\n .join(\"\");\n\n return `${newBlock.slice(0, insertPos)}${linksXml}\\n ${newBlock.slice(insertPos).trimStart()}`;\n}\n\nexport function normalizeTrailingSlashInBlock(\n urlBlock: string,\n policy: \"preserve\" | \"always\" | \"never\",\n): string {\n if (policy === \"preserve\") return urlBlock;\n\n // Normalize <loc>\n let result = urlBlock.replace(/<loc>([^<]+)<\\/loc>/g, (_, url: string) => {\n return `<loc>${applyTrailingSlashPolicy(url, policy)}</loc>`;\n });\n\n // Normalize href in xhtml:link\n result = result.replace(\n /(<xhtml:link[^>]*href=\")([^\"]+)(\"[^>]*\\/>)/g,\n (_, prefix: string, url: string, suffix: string) => {\n return `${prefix}${applyTrailingSlashPolicy(url, policy)}${suffix}`;\n },\n );\n\n return result;\n}\n\nfunction applyTrailingSlashPolicy(url: string, policy: \"always\" | \"never\"): string {\n try {\n const u = new URL(url);\n if (policy === \"always\" && !u.pathname.endsWith(\"/\")) {\n u.pathname += \"/\";\n } else if (policy === \"never\" && u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) {\n u.pathname = u.pathname.slice(0, -1);\n }\n return u.toString();\n } catch {\n return url;\n }\n}\n","import { getOriginFromAbsoluteUrl, resolveAbsoluteUrl } from \"../lib/url.js\";\nimport type { XDefaultStrategy } from \"../lib/types.js\";\nimport {\n ensureXhtmlNamespace,\n extractLoc,\n extractUrlBlocks,\n extractXhtmlLinks,\n hasXDefault,\n insertXhtmlLink,\n normalizeTrailingSlashInBlock,\n reorderXhtmlLinks,\n xmlEscape,\n} from \"./xml.js\";\n\nexport type InjectOptions = {\n baseUrl?: string;\n xDefaultStrategy?: XDefaultStrategy;\n ensureNamespace?: boolean;\n canonicalLocale?: string;\n order?: \"canonical-first\" | \"preserve\";\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport function injectXDefaultIntoSitemapXml(xml: string, options: InjectOptions): string {\n const ensureNamespace = options.ensureNamespace ?? true;\n const xDefaultStrategy = options.xDefaultStrategy ?? { type: \"loc\" };\n const order = options.order ?? \"preserve\";\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n\n const xmlWithNs = ensureNamespace ? ensureXhtmlNamespace(xml) : xml;\n\n const blocks = extractUrlBlocks(xmlWithNs);\n if (blocks.length === 0) return xmlWithNs;\n\n let out = xmlWithNs;\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n let nextBlock = block;\n\n if (!hasXDefault(block)) {\n const href = resolveXDefaultHref({\n loc,\n links,\n ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),\n strategy: xDefaultStrategy,\n });\n\n const linkXml = `<xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"${xmlEscape(href)}\" />`;\n nextBlock = insertXhtmlLink(nextBlock, linkXml);\n }\n\n // Apply reordering if needed\n if (order === \"canonical-first\") {\n nextBlock = reorderXhtmlLinks(nextBlock, {\n ...(options.canonicalLocale ? { canonicalLocale: options.canonicalLocale } : {}),\n order: \"canonical-first\",\n });\n }\n\n // Apply trailing slash normalization\n if (trailingSlash !== \"preserve\") {\n nextBlock = normalizeTrailingSlashInBlock(nextBlock, trailingSlash);\n }\n\n out = out.replace(block, nextBlock);\n }\n\n return out;\n}\n\nfunction resolveXDefaultHref(args: {\n loc: string;\n links: Array<{ hreflang: string; href: string }>;\n baseUrl?: string;\n strategy: XDefaultStrategy;\n}): string {\n const { loc, links, baseUrl, strategy } = args;\n\n const locAbs = baseUrl ? resolveAbsoluteUrl(loc, baseUrl) : loc;\n const origin = isAbsolute(locAbs) ? getOriginFromAbsoluteUrl(locAbs) : baseUrl;\n\n if (strategy.type === \"loc\") return locAbs;\n if (strategy.type === \"root\") {\n if (!origin) return locAbs;\n return resolveAbsoluteUrl(\"/\", origin);\n }\n if (strategy.type === \"custom\") {\n if (!origin) return strategy.url;\n return resolveAbsoluteUrl(strategy.url, origin);\n }\n if (strategy.type === \"locale\") {\n const found = links.find((l) => l.hreflang === strategy.locale)?.href;\n if (found) return baseUrl ? resolveAbsoluteUrl(found, baseUrl) : found;\n return locAbs;\n }\n const computed = strategy.resolve({ url: locAbs });\n if (!origin) return computed;\n return resolveAbsoluteUrl(computed, origin);\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n","import type { HreflangIssue, HreflangReport } from \"../lib/types.js\";\nimport { extractLoc, extractUrlBlocks, extractXhtmlLinks, hasXhtmlNamespace } from \"./xml.js\";\n\nexport type CheckXmlOptions = {\n requireNamespace?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireAbsolute?: boolean;\n checkDuplicateKeys?: boolean;\n checkDuplicateHrefs?: boolean;\n checkHreflangCasing?: boolean;\n originPolicy?: \"same\" | \"allowlist\" | \"off\";\n allowedOrigins?: string[];\n};\n\nexport function checkSitemapXmlHreflang(xml: string, options: CheckXmlOptions): HreflangReport {\n const requireNamespace = options.requireNamespace ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireAbsolute = options.requireAbsolute ?? true;\n const checkDuplicateKeys = options.checkDuplicateKeys ?? true;\n const checkDuplicateHrefs = options.checkDuplicateHrefs ?? true;\n const checkHreflangCasing = options.checkHreflangCasing ?? true;\n const originPolicy = options.originPolicy ?? \"off\";\n\n const issues: HreflangIssue[] = [];\n\n const blocks = extractUrlBlocks(xml);\n\n if (requireNamespace) {\n const usesXhtml = /<xhtml:link\\b/.test(xml);\n if (usesXhtml && !hasXhtmlNamespace(xml)) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: \"sitemap.xml\",\n message: 'Missing xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" in <urlset>',\n });\n }\n }\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n const hasXDefault = links.some((l) => l.hreflang === \"x-default\");\n if (requireXDefaultWhenMultiple && links.length > 1 && !hasXDefault) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: loc,\n message: \"Missing x-default hreflang in sitemap url block\",\n });\n }\n\n // Check for duplicate hreflang keys\n if (checkDuplicateKeys) {\n const seenKeys = new Set<string>();\n for (const link of links) {\n if (seenKeys.has(link.hreflang)) {\n issues.push({\n code: \"DUPLICATE_HREFLANG_KEY\",\n entryUrl: loc,\n message: `Duplicate hreflang key: ${link.hreflang}`,\n });\n }\n seenKeys.add(link.hreflang);\n }\n }\n\n // Check for duplicate hrefs (excluding x-default which can share href with another locale)\n if (checkDuplicateHrefs) {\n const hrefToLocales = new Map<string, string[]>();\n for (const link of links) {\n const locales = hrefToLocales.get(link.href) ?? [];\n locales.push(link.hreflang);\n hrefToLocales.set(link.href, locales);\n }\n for (const [href, locales] of hrefToLocales) {\n // Filter out x-default - it's allowed to share href with another locale\n const nonXDefaultLocales = locales.filter((l) => l !== \"x-default\");\n if (nonXDefaultLocales.length > 1) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: loc,\n message: `Duplicate hreflang href detected: ${href} (locales: ${nonXDefaultLocales.join(\", \")})`,\n });\n }\n }\n }\n\n // Check hreflang casing\n if (checkHreflangCasing) {\n for (const link of links) {\n if (!isValidHreflangCasing(link.hreflang)) {\n issues.push({\n code: \"INVALID_HREFLANG_CASING\",\n entryUrl: loc,\n message: `Invalid hreflang casing: ${link.hreflang}. Expected format: en, pt-BR, or x-default`,\n });\n }\n }\n }\n\n // Check origin policy\n if (originPolicy === \"same\") {\n const locOrigin = getOrigin(loc);\n if (locOrigin) {\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && linkOrigin !== locOrigin) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Inconsistent origin for ${link.hreflang}: expected ${locOrigin}, got ${linkOrigin}`,\n });\n }\n }\n }\n } else if (originPolicy === \"allowlist\") {\n const allowedOrigins = options.allowedOrigins ?? [];\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && !allowedOrigins.includes(linkOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for ${link.hreflang}: ${linkOrigin}`,\n });\n }\n }\n const locOrigin = getOrigin(loc);\n if (locOrigin && !allowedOrigins.includes(locOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for loc: ${locOrigin}`,\n });\n }\n }\n\n if (requireAbsolute) {\n for (const link of links) {\n if (!isAbsolute(link.href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute hreflang href for ${link.hreflang}: ${link.href}`,\n });\n }\n }\n if (!isAbsolute(loc)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute <loc>: ${loc}`,\n });\n }\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n\nfunction getOrigin(url: string): string | null {\n try {\n const u = new URL(url);\n return u.origin;\n } catch {\n return null;\n }\n}\n\nfunction isValidHreflangCasing(key: string): boolean {\n if (key === \"x-default\") return true;\n // en, de, uk - only lowercase\n if (/^[a-z]{2}$/.test(key)) return true;\n // pt-BR, en-US - lowercase-UPPERCASE\n if (/^[a-z]{2}-[A-Z]{2}$/.test(key)) return true;\n return false;\n}\n"],"mappings":"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","names":["href","normalizedPath","hasXDefault","isAbsolute"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pas7/nextjs-sitemap-hreflang",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Add and validate hreflang alternates + x-default for Next.js sitemaps (App Router / MetadataRoute) with a tiny library + CLI postbuild fixer.",
|
|
5
5
|
"homepage": "https://github.com/pas7-studio/nextjs-sitemap-hreflang#readme",
|
|
6
6
|
"repository": {
|
|
@@ -66,7 +66,10 @@
|
|
|
66
66
|
"@eslint/js": "^9.0.0",
|
|
67
67
|
"@types/node": "^20.0.0",
|
|
68
68
|
"eslint": "^9.0.0",
|
|
69
|
+
"next": "^15.0.0",
|
|
69
70
|
"prettier": "^3.0.0",
|
|
71
|
+
"react": "^19.0.0",
|
|
72
|
+
"react-dom": "^19.0.0",
|
|
70
73
|
"tsup": "^8.0.0",
|
|
71
74
|
"typescript": "^5.0.0",
|
|
72
75
|
"typescript-eslint": "^8.0.0",
|