@puzzle-ui/seo 0.0.0-stage → 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 +755 -2
- package/dist/LICENSE +21 -0
- package/dist/context/config.cjs +82 -0
- package/dist/context/config.cjs.map +1 -0
- package/dist/context/config.d.cts +6 -0
- package/dist/context/config.d.ts +6 -0
- package/dist/context/config.js +80 -0
- package/dist/context/config.js.map +1 -0
- package/dist/context/index.cjs +6 -0
- package/dist/context/index.d.cts +4 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.js +3 -0
- package/dist/context/types.d.cts +46 -0
- package/dist/context/types.d.ts +46 -0
- package/dist/core/graph.cjs +464 -0
- package/dist/core/graph.cjs.map +1 -0
- package/dist/core/graph.d.cts +62 -0
- package/dist/core/graph.d.ts +62 -0
- package/dist/core/graph.js +456 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/index.cjs +11 -0
- package/dist/core/index.d.cts +3 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +1 -0
- package/dist/core/types.d.cts +92 -0
- package/dist/core/types.d.ts +92 -0
- package/dist/index.cjs +96 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata/index.cjs +4 -0
- package/dist/metadata/index.d.cts +3 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.js +1 -0
- package/dist/metadata/resolve.cjs +122 -0
- package/dist/metadata/resolve.cjs.map +1 -0
- package/dist/metadata/resolve.d.cts +4 -0
- package/dist/metadata/resolve.d.ts +4 -0
- package/dist/metadata/resolve.js +121 -0
- package/dist/metadata/resolve.js.map +1 -0
- package/dist/metadata/types.d.cts +82 -0
- package/dist/metadata/types.d.ts +82 -0
- package/dist/next/index.cjs +13 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +15 -0
- package/dist/next/index.d.ts +14 -0
- package/dist/next/index.js +12 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/context.cjs +30 -0
- package/dist/react/context.cjs.map +1 -0
- package/dist/react/context.d.cts +14 -0
- package/dist/react/context.d.ts +14 -0
- package/dist/react/context.js +27 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/index.cjs +48 -0
- package/dist/react/index.d.cts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +5 -0
- package/dist/react/metadata.cjs +100 -0
- package/dist/react/metadata.cjs.map +1 -0
- package/dist/react/metadata.d.cts +18 -0
- package/dist/react/metadata.d.ts +18 -0
- package/dist/react/metadata.js +99 -0
- package/dist/react/metadata.js.map +1 -0
- package/dist/react/provider.cjs +42 -0
- package/dist/react/provider.cjs.map +1 -0
- package/dist/react/provider.d.cts +3 -0
- package/dist/react/provider.d.ts +3 -0
- package/dist/react/provider.js +42 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/schema-components.cjs +294 -0
- package/dist/react/schema-components.cjs.map +1 -0
- package/dist/react/schema-components.d.cts +84 -0
- package/dist/react/schema-components.d.ts +80 -0
- package/dist/react/schema-components.js +254 -0
- package/dist/react/schema-components.js.map +1 -0
- package/dist/react/types.d.cts +81 -0
- package/dist/react/types.d.ts +80 -0
- package/dist/react-router/index.cjs +13 -0
- package/dist/react-router/index.cjs.map +1 -0
- package/dist/react-router/index.d.cts +21 -0
- package/dist/react-router/index.d.ts +20 -0
- package/dist/react-router/index.js +12 -0
- package/dist/react-router/index.js.map +1 -0
- package/dist/robots/generate.cjs +88 -0
- package/dist/robots/generate.cjs.map +1 -0
- package/dist/robots/generate.d.cts +4 -0
- package/dist/robots/generate.d.ts +4 -0
- package/dist/robots/generate.js +86 -0
- package/dist/robots/generate.js.map +1 -0
- package/dist/robots/index.cjs +5 -0
- package/dist/robots/index.d.cts +3 -0
- package/dist/robots/index.d.ts +3 -0
- package/dist/robots/index.js +1 -0
- package/dist/robots/types.d.cts +27 -0
- package/dist/robots/types.d.ts +27 -0
- package/dist/schema/create-registry.cjs +12 -0
- package/dist/schema/create-registry.cjs.map +1 -0
- package/dist/schema/create-registry.d.cts +2 -0
- package/dist/schema/create-registry.d.ts +2 -0
- package/dist/schema/create-registry.js +12 -0
- package/dist/schema/create-registry.js.map +1 -0
- package/dist/schema/generated/index.d.cts +2 -0
- package/dist/schema/generated/schemaorg.cjs +36386 -0
- package/dist/schema/generated/schemaorg.cjs.map +1 -0
- package/dist/schema/generated/schemaorg.d.cts +945 -0
- package/dist/schema/generated/schemaorg.d.ts +945 -0
- package/dist/schema/generated/schemaorg.js +36384 -0
- package/dist/schema/generated/schemaorg.js.map +1 -0
- package/dist/schema/index.cjs +11 -0
- package/dist/schema/index.d.cts +7 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/registry.cjs +194 -0
- package/dist/schema/registry.cjs.map +1 -0
- package/dist/schema/registry.d.cts +2 -0
- package/dist/schema/registry.d.ts +2 -0
- package/dist/schema/registry.js +194 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/schema/types.d.cts +79 -0
- package/dist/schema/types.d.ts +79 -0
- package/dist/schema/validate.cjs +131 -0
- package/dist/schema/validate.cjs.map +1 -0
- package/dist/schema/validate.d.cts +16 -0
- package/dist/schema/validate.d.ts +16 -0
- package/dist/schema/validate.js +131 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/server/handlers.cjs +96 -0
- package/dist/server/handlers.cjs.map +1 -0
- package/dist/server/handlers.d.cts +3 -0
- package/dist/server/handlers.d.ts +3 -0
- package/dist/server/handlers.js +95 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/index.cjs +6 -0
- package/dist/server/index.d.cts +4 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +3 -0
- package/dist/server/routes.cjs +17 -0
- package/dist/server/routes.cjs.map +1 -0
- package/dist/server/routes.d.cts +2 -0
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.js +17 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/types.d.cts +61 -0
- package/dist/server/types.d.ts +61 -0
- package/dist/sitemap/generate.cjs +147 -0
- package/dist/sitemap/generate.cjs.map +1 -0
- package/dist/sitemap/generate.d.cts +3 -0
- package/dist/sitemap/generate.d.ts +3 -0
- package/dist/sitemap/generate.js +146 -0
- package/dist/sitemap/generate.js.map +1 -0
- package/dist/sitemap/index.cjs +11 -0
- package/dist/sitemap/index.d.cts +4 -0
- package/dist/sitemap/index.d.ts +4 -0
- package/dist/sitemap/index.js +3 -0
- package/dist/sitemap/types.d.cts +82 -0
- package/dist/sitemap/types.d.ts +82 -0
- package/dist/sitemap/xml.cjs +215 -0
- package/dist/sitemap/xml.cjs.map +1 -0
- package/dist/sitemap/xml.d.cts +16 -0
- package/dist/sitemap/xml.d.ts +16 -0
- package/dist/sitemap/xml.js +210 -0
- package/dist/sitemap/xml.js.map +1 -0
- package/dist/tanstack/index.cjs +13 -0
- package/dist/tanstack/index.cjs.map +1 -0
- package/dist/tanstack/index.d.cts +19 -0
- package/dist/tanstack/index.d.ts +18 -0
- package/dist/tanstack/index.js +12 -0
- package/dist/tanstack/index.js.map +1 -0
- package/dist/testing/diagnostics.cjs +27 -0
- package/dist/testing/diagnostics.cjs.map +1 -0
- package/dist/testing/diagnostics.d.cts +3 -0
- package/dist/testing/diagnostics.d.ts +3 -0
- package/dist/testing/diagnostics.js +27 -0
- package/dist/testing/diagnostics.js.map +1 -0
- package/dist/testing/fixture.cjs +80 -0
- package/dist/testing/fixture.cjs.map +1 -0
- package/dist/testing/fixture.d.cts +4 -0
- package/dist/testing/fixture.d.ts +4 -0
- package/dist/testing/fixture.js +78 -0
- package/dist/testing/fixture.js.map +1 -0
- package/dist/testing/index.cjs +9 -0
- package/dist/testing/index.d.cts +5 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/inspect.cjs +63 -0
- package/dist/testing/inspect.cjs.map +1 -0
- package/dist/testing/inspect.d.cts +5 -0
- package/dist/testing/inspect.d.ts +5 -0
- package/dist/testing/inspect.js +63 -0
- package/dist/testing/inspect.js.map +1 -0
- package/dist/testing/types.d.cts +40 -0
- package/dist/testing/types.d.ts +40 -0
- package/dist/urls/index.cjs +8 -0
- package/dist/urls/index.d.cts +3 -0
- package/dist/urls/index.d.ts +3 -0
- package/dist/urls/index.js +1 -0
- package/dist/urls/normalize.cjs +65 -0
- package/dist/urls/normalize.cjs.map +1 -0
- package/dist/urls/normalize.d.cts +14 -0
- package/dist/urls/normalize.d.ts +14 -0
- package/dist/urls/normalize.js +60 -0
- package/dist/urls/normalize.js.map +1 -0
- package/dist/urls/types.d.cts +22 -0
- package/dist/urls/types.d.ts +22 -0
- package/dist/validation/index.cjs +5 -0
- package/dist/validation/index.d.cts +3 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/types.d.cts +75 -0
- package/dist/validation/types.d.ts +75 -0
- package/dist/validation/validate.cjs +452 -0
- package/dist/validation/validate.cjs.map +1 -0
- package/dist/validation/validate.d.cts +7 -0
- package/dist/validation/validate.d.ts +6 -0
- package/dist/validation/validate.js +450 -0
- package/dist/validation/validate.js.map +1 -0
- package/package.json +203 -4
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/urls/normalize.ts
|
|
2
|
+
const normalizeSeoPath = (pathname, trailingSlash = "never") => {
|
|
3
|
+
const path = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
4
|
+
if (path === "/") return "/";
|
|
5
|
+
if (trailingSlash === "always") return `${path.replace(/\/+$/, "")}/`;
|
|
6
|
+
if (trailingSlash === "never") return path.replace(/\/+$/, "");
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
const normalizeSeoUrl = (value, options = {}) => {
|
|
10
|
+
if (typeof value !== "string" || !value.trim()) throw new Error("SEO URL must be a non-empty string.");
|
|
11
|
+
const baseUrl = options.baseUrl ?? "https://example.com";
|
|
12
|
+
let url;
|
|
13
|
+
let base;
|
|
14
|
+
try {
|
|
15
|
+
base = new URL(baseUrl);
|
|
16
|
+
url = new URL(value, base);
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(`Invalid SEO URL: "${value}".`);
|
|
19
|
+
}
|
|
20
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("SEO URLs must use HTTP or HTTPS.");
|
|
21
|
+
if (url.username || url.password) {
|
|
22
|
+
if (!options.allowCredentials) throw new Error("SEO URLs must not contain credentials.");
|
|
23
|
+
}
|
|
24
|
+
if (options.allowExternal === false && url.origin !== base.origin) throw new Error("External SEO URLs are not allowed.");
|
|
25
|
+
url.pathname = normalizeSeoPath(url.pathname, options.trailingSlash ?? "never");
|
|
26
|
+
return url.toString();
|
|
27
|
+
};
|
|
28
|
+
const normalizeSeoOrigin = (value) => {
|
|
29
|
+
let url;
|
|
30
|
+
try {
|
|
31
|
+
url = new URL(value);
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(`Invalid SEO origin: "${value}".`);
|
|
34
|
+
}
|
|
35
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("SEO origins must use HTTP or HTTPS.");
|
|
36
|
+
if (url.username || url.password) throw new Error("SEO origins must not contain credentials.");
|
|
37
|
+
return url.origin;
|
|
38
|
+
};
|
|
39
|
+
const isSameSeoOrigin = (first, second) => {
|
|
40
|
+
try {
|
|
41
|
+
return new URL(first).origin === new URL(second).origin;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const isValidSeoUrl = (value, baseUrl) => {
|
|
47
|
+
try {
|
|
48
|
+
normalizeSeoUrl(value, { baseUrl });
|
|
49
|
+
return true;
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const resolveSeoUrls = (values, options = {}) => {
|
|
55
|
+
const resolved = values.map((value) => normalizeSeoUrl(value, options));
|
|
56
|
+
return [...new Set(resolved)].sort((a, b) => a.localeCompare(b));
|
|
57
|
+
};
|
|
58
|
+
exports.isSameSeoOrigin = isSameSeoOrigin;
|
|
59
|
+
exports.isValidSeoUrl = isValidSeoUrl;
|
|
60
|
+
exports.normalizeSeoOrigin = normalizeSeoOrigin;
|
|
61
|
+
exports.normalizeSeoPath = normalizeSeoPath;
|
|
62
|
+
exports.normalizeSeoUrl = normalizeSeoUrl;
|
|
63
|
+
exports.resolveSeoUrls = resolveSeoUrls;
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=normalize.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.cjs","names":[],"sources":["../../src/urls/normalize.ts"],"sourcesContent":["import type { SeoUrl } from \"../core/types\";\nimport type { SeoTrailingSlash } from \"../context/types\";\n\nexport interface NormalizeSeoUrlOptions {\n baseUrl?: string;\n trailingSlash?: SeoTrailingSlash;\n allowExternal?: boolean;\n allowCredentials?: boolean;\n}\n\nexport const normalizeSeoPath = (\n pathname: string,\n trailingSlash: SeoTrailingSlash = \"never\",\n): string => {\n const path = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n\n if (path === \"/\") {\n return \"/\";\n }\n\n if (trailingSlash === \"always\") {\n return `${path.replace(/\\/+$/, \"\")}/`;\n }\n\n if (trailingSlash === \"never\") {\n return path.replace(/\\/+$/, \"\");\n }\n\n return path;\n};\n\nexport const normalizeSeoUrl = (\n value: string,\n options: NormalizeSeoUrlOptions = {},\n): SeoUrl => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(\"SEO URL must be a non-empty string.\");\n }\n\n const baseUrl = options.baseUrl ?? \"https://example.com\";\n\n let url: URL;\n let base: URL;\n\n try {\n base = new URL(baseUrl);\n url = new URL(value, base);\n } catch {\n throw new Error(`Invalid SEO URL: \"${value}\".`);\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"SEO URLs must use HTTP or HTTPS.\");\n }\n\n if (url.username || url.password) {\n if (!options.allowCredentials) {\n throw new Error(\"SEO URLs must not contain credentials.\");\n }\n }\n\n if (options.allowExternal === false && url.origin !== base.origin) {\n throw new Error(\"External SEO URLs are not allowed.\");\n }\n\n url.pathname = normalizeSeoPath(\n url.pathname,\n options.trailingSlash ?? \"never\",\n );\n\n return url.toString() as SeoUrl;\n};\n\nexport const normalizeSeoOrigin = (value: string): SeoUrl => {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(`Invalid SEO origin: \"${value}\".`);\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"SEO origins must use HTTP or HTTPS.\");\n }\n\n if (url.username || url.password) {\n throw new Error(\"SEO origins must not contain credentials.\");\n }\n\n return url.origin as SeoUrl;\n};\n\nexport const isSameSeoOrigin = (first: string, second: string): boolean => {\n try {\n return new URL(first).origin === new URL(second).origin;\n } catch {\n return false;\n }\n};\n\nexport const isValidSeoUrl = (value: string, baseUrl?: string): boolean => {\n try {\n normalizeSeoUrl(value, { baseUrl });\n return true;\n } catch {\n return false;\n }\n};\n\nexport const resolveSeoUrls = (\n values: readonly string[],\n options: NormalizeSeoUrlOptions = {},\n): SeoUrl[] => {\n const resolved = values.map((value) => normalizeSeoUrl(value, options));\n\n return [...new Set(resolved)].sort((a, b) => a.localeCompare(b));\n};\n"],"mappings":";AAUA,MAAa,oBACX,UACA,gBAAkC,YACvB;CACX,MAAM,OAAO,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI;CAEvD,IAAI,SAAS,KACX,OAAO;CAGT,IAAI,kBAAkB,UACpB,OAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,EAAE;CAGrC,IAAI,kBAAkB,SACpB,OAAO,KAAK,QAAQ,QAAQ,EAAE;CAGhC,OAAO;AACT;AAEA,MAAa,mBACX,OACA,UAAkC,CAAC,MACxB;CACX,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAC3C,MAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,UAAU,QAAQ,WAAW;CAEnC,IAAI;CACJ,IAAI;CAEJ,IAAI;EACF,OAAO,IAAI,IAAI,OAAO;EACtB,MAAM,IAAI,IAAI,OAAO,IAAI;CAC3B,QAAQ;EACN,MAAM,IAAI,MAAM,qBAAqB,MAAM,GAAG;CAChD;CAEA,IAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAC/C,MAAM,IAAI,MAAM,kCAAkC;CAGpD,IAAI,IAAI,YAAY,IAAI,UAClB;MAAA,CAAC,QAAQ,kBACX,MAAM,IAAI,MAAM,wCAAwC;CAAA;CAI5D,IAAI,QAAQ,kBAAkB,SAAS,IAAI,WAAW,KAAK,QACzD,MAAM,IAAI,MAAM,oCAAoC;CAGtD,IAAI,WAAW,iBACb,IAAI,UACJ,QAAQ,iBAAiB,OAC3B;CAEA,OAAO,IAAI,SAAS;AACtB;AAEA,MAAa,sBAAsB,UAA0B;CAC3D,IAAI;CAEJ,IAAI;EACF,MAAM,IAAI,IAAI,KAAK;CACrB,QAAQ;EACN,MAAM,IAAI,MAAM,wBAAwB,MAAM,GAAG;CACnD;CAEA,IAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAC/C,MAAM,IAAI,MAAM,qCAAqC;CAGvD,IAAI,IAAI,YAAY,IAAI,UACtB,MAAM,IAAI,MAAM,2CAA2C;CAG7D,OAAO,IAAI;AACb;AAEA,MAAa,mBAAmB,OAAe,WAA4B;CACzE,IAAI;EACF,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC;CACnD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,iBAAiB,OAAe,YAA8B;CACzE,IAAI;EACF,gBAAgB,OAAO,EAAE,QAAQ,CAAC;EAClC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,kBACX,QACA,UAAkC,CAAC,MACtB;CACb,MAAM,WAAW,OAAO,KAAK,UAAU,gBAAgB,OAAO,OAAO,CAAC;CAEtE,OAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACjE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SeoUrl } from "../core/types.cjs";
|
|
2
|
+
import { SeoTrailingSlash } from "../context/types.cjs";
|
|
3
|
+
export interface NormalizeSeoUrlOptions {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
trailingSlash?: SeoTrailingSlash;
|
|
6
|
+
allowExternal?: boolean;
|
|
7
|
+
allowCredentials?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const normalizeSeoPath: (pathname: string, trailingSlash?: SeoTrailingSlash) => string;
|
|
10
|
+
export declare const normalizeSeoUrl: (value: string, options?: NormalizeSeoUrlOptions) => SeoUrl;
|
|
11
|
+
export declare const normalizeSeoOrigin: (value: string) => SeoUrl;
|
|
12
|
+
export declare const isSameSeoOrigin: (first: string, second: string) => boolean;
|
|
13
|
+
export declare const isValidSeoUrl: (value: string, baseUrl?: string) => boolean;
|
|
14
|
+
export declare const resolveSeoUrls: (values: readonly string[], options?: NormalizeSeoUrlOptions) => SeoUrl[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SeoUrl } from "../core/types.js";
|
|
2
|
+
import { SeoTrailingSlash } from "../context/types.js";
|
|
3
|
+
export interface NormalizeSeoUrlOptions {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
trailingSlash?: SeoTrailingSlash;
|
|
6
|
+
allowExternal?: boolean;
|
|
7
|
+
allowCredentials?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const normalizeSeoPath: (pathname: string, trailingSlash?: SeoTrailingSlash) => string;
|
|
10
|
+
export declare const normalizeSeoUrl: (value: string, options?: NormalizeSeoUrlOptions) => SeoUrl;
|
|
11
|
+
export declare const normalizeSeoOrigin: (value: string) => SeoUrl;
|
|
12
|
+
export declare const isSameSeoOrigin: (first: string, second: string) => boolean;
|
|
13
|
+
export declare const isValidSeoUrl: (value: string, baseUrl?: string) => boolean;
|
|
14
|
+
export declare const resolveSeoUrls: (values: readonly string[], options?: NormalizeSeoUrlOptions) => SeoUrl[];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/urls/normalize.ts
|
|
2
|
+
const normalizeSeoPath = (pathname, trailingSlash = "never") => {
|
|
3
|
+
const path = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
4
|
+
if (path === "/") return "/";
|
|
5
|
+
if (trailingSlash === "always") return `${path.replace(/\/+$/, "")}/`;
|
|
6
|
+
if (trailingSlash === "never") return path.replace(/\/+$/, "");
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
const normalizeSeoUrl = (value, options = {}) => {
|
|
10
|
+
if (typeof value !== "string" || !value.trim()) throw new Error("SEO URL must be a non-empty string.");
|
|
11
|
+
const baseUrl = options.baseUrl ?? "https://example.com";
|
|
12
|
+
let url;
|
|
13
|
+
let base;
|
|
14
|
+
try {
|
|
15
|
+
base = new URL(baseUrl);
|
|
16
|
+
url = new URL(value, base);
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(`Invalid SEO URL: "${value}".`);
|
|
19
|
+
}
|
|
20
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("SEO URLs must use HTTP or HTTPS.");
|
|
21
|
+
if (url.username || url.password) {
|
|
22
|
+
if (!options.allowCredentials) throw new Error("SEO URLs must not contain credentials.");
|
|
23
|
+
}
|
|
24
|
+
if (options.allowExternal === false && url.origin !== base.origin) throw new Error("External SEO URLs are not allowed.");
|
|
25
|
+
url.pathname = normalizeSeoPath(url.pathname, options.trailingSlash ?? "never");
|
|
26
|
+
return url.toString();
|
|
27
|
+
};
|
|
28
|
+
const normalizeSeoOrigin = (value) => {
|
|
29
|
+
let url;
|
|
30
|
+
try {
|
|
31
|
+
url = new URL(value);
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(`Invalid SEO origin: "${value}".`);
|
|
34
|
+
}
|
|
35
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("SEO origins must use HTTP or HTTPS.");
|
|
36
|
+
if (url.username || url.password) throw new Error("SEO origins must not contain credentials.");
|
|
37
|
+
return url.origin;
|
|
38
|
+
};
|
|
39
|
+
const isSameSeoOrigin = (first, second) => {
|
|
40
|
+
try {
|
|
41
|
+
return new URL(first).origin === new URL(second).origin;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const isValidSeoUrl = (value, baseUrl) => {
|
|
47
|
+
try {
|
|
48
|
+
normalizeSeoUrl(value, { baseUrl });
|
|
49
|
+
return true;
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const resolveSeoUrls = (values, options = {}) => {
|
|
55
|
+
const resolved = values.map((value) => normalizeSeoUrl(value, options));
|
|
56
|
+
return [...new Set(resolved)].sort((a, b) => a.localeCompare(b));
|
|
57
|
+
};
|
|
58
|
+
export { isSameSeoOrigin, isValidSeoUrl, normalizeSeoOrigin, normalizeSeoPath, normalizeSeoUrl, resolveSeoUrls };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","names":[],"sources":["../../src/urls/normalize.ts"],"sourcesContent":["import type { SeoUrl } from \"../core/types\";\nimport type { SeoTrailingSlash } from \"../context/types\";\n\nexport interface NormalizeSeoUrlOptions {\n baseUrl?: string;\n trailingSlash?: SeoTrailingSlash;\n allowExternal?: boolean;\n allowCredentials?: boolean;\n}\n\nexport const normalizeSeoPath = (\n pathname: string,\n trailingSlash: SeoTrailingSlash = \"never\",\n): string => {\n const path = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n\n if (path === \"/\") {\n return \"/\";\n }\n\n if (trailingSlash === \"always\") {\n return `${path.replace(/\\/+$/, \"\")}/`;\n }\n\n if (trailingSlash === \"never\") {\n return path.replace(/\\/+$/, \"\");\n }\n\n return path;\n};\n\nexport const normalizeSeoUrl = (\n value: string,\n options: NormalizeSeoUrlOptions = {},\n): SeoUrl => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(\"SEO URL must be a non-empty string.\");\n }\n\n const baseUrl = options.baseUrl ?? \"https://example.com\";\n\n let url: URL;\n let base: URL;\n\n try {\n base = new URL(baseUrl);\n url = new URL(value, base);\n } catch {\n throw new Error(`Invalid SEO URL: \"${value}\".`);\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"SEO URLs must use HTTP or HTTPS.\");\n }\n\n if (url.username || url.password) {\n if (!options.allowCredentials) {\n throw new Error(\"SEO URLs must not contain credentials.\");\n }\n }\n\n if (options.allowExternal === false && url.origin !== base.origin) {\n throw new Error(\"External SEO URLs are not allowed.\");\n }\n\n url.pathname = normalizeSeoPath(\n url.pathname,\n options.trailingSlash ?? \"never\",\n );\n\n return url.toString() as SeoUrl;\n};\n\nexport const normalizeSeoOrigin = (value: string): SeoUrl => {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(`Invalid SEO origin: \"${value}\".`);\n }\n\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"SEO origins must use HTTP or HTTPS.\");\n }\n\n if (url.username || url.password) {\n throw new Error(\"SEO origins must not contain credentials.\");\n }\n\n return url.origin as SeoUrl;\n};\n\nexport const isSameSeoOrigin = (first: string, second: string): boolean => {\n try {\n return new URL(first).origin === new URL(second).origin;\n } catch {\n return false;\n }\n};\n\nexport const isValidSeoUrl = (value: string, baseUrl?: string): boolean => {\n try {\n normalizeSeoUrl(value, { baseUrl });\n return true;\n } catch {\n return false;\n }\n};\n\nexport const resolveSeoUrls = (\n values: readonly string[],\n options: NormalizeSeoUrlOptions = {},\n): SeoUrl[] => {\n const resolved = values.map((value) => normalizeSeoUrl(value, options));\n\n return [...new Set(resolved)].sort((a, b) => a.localeCompare(b));\n};\n"],"mappings":";AAUA,MAAa,oBACX,UACA,gBAAkC,YACvB;CACX,MAAM,OAAO,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI;CAEvD,IAAI,SAAS,KACX,OAAO;CAGT,IAAI,kBAAkB,UACpB,OAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,EAAE;CAGrC,IAAI,kBAAkB,SACpB,OAAO,KAAK,QAAQ,QAAQ,EAAE;CAGhC,OAAO;AACT;AAEA,MAAa,mBACX,OACA,UAAkC,CAAC,MACxB;CACX,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAC3C,MAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,UAAU,QAAQ,WAAW;CAEnC,IAAI;CACJ,IAAI;CAEJ,IAAI;EACF,OAAO,IAAI,IAAI,OAAO;EACtB,MAAM,IAAI,IAAI,OAAO,IAAI;CAC3B,QAAQ;EACN,MAAM,IAAI,MAAM,qBAAqB,MAAM,GAAG;CAChD;CAEA,IAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAC/C,MAAM,IAAI,MAAM,kCAAkC;CAGpD,IAAI,IAAI,YAAY,IAAI,UAClB;MAAA,CAAC,QAAQ,kBACX,MAAM,IAAI,MAAM,wCAAwC;CAAA;CAI5D,IAAI,QAAQ,kBAAkB,SAAS,IAAI,WAAW,KAAK,QACzD,MAAM,IAAI,MAAM,oCAAoC;CAGtD,IAAI,WAAW,iBACb,IAAI,UACJ,QAAQ,iBAAiB,OAC3B;CAEA,OAAO,IAAI,SAAS;AACtB;AAEA,MAAa,sBAAsB,UAA0B;CAC3D,IAAI;CAEJ,IAAI;EACF,MAAM,IAAI,IAAI,KAAK;CACrB,QAAQ;EACN,MAAM,IAAI,MAAM,wBAAwB,MAAM,GAAG;CACnD;CAEA,IAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAC/C,MAAM,IAAI,MAAM,qCAAqC;CAGvD,IAAI,IAAI,YAAY,IAAI,UACtB,MAAM,IAAI,MAAM,2CAA2C;CAG7D,OAAO,IAAI;AACb;AAEA,MAAa,mBAAmB,OAAe,WAA4B;CACzE,IAAI;EACF,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC,WAAW,IAAI,IAAI,MAAM,CAAC,CAAC;CACnD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,iBAAiB,OAAe,YAA8B;CACzE,IAAI;EACF,gBAAgB,OAAO,EAAE,QAAQ,CAAC;EAClC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,kBACX,QACA,UAAkC,CAAC,MACtB;CACb,MAAM,WAAW,OAAO,KAAK,UAAU,gBAAgB,OAAO,OAAO,CAAC;CAEtE,OAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACjE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SeoUrl } from "../core/types.cjs";
|
|
2
|
+
import { SeoTrailingSlash } from "../context/types.cjs";
|
|
3
|
+
export interface SeoUrlOptions {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
trailingSlash?: SeoTrailingSlash;
|
|
6
|
+
allowExternal?: boolean;
|
|
7
|
+
allowCredentials?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface SeoUrlValidationIssue {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SeoUrlResult {
|
|
15
|
+
url?: SeoUrl;
|
|
16
|
+
valid: boolean;
|
|
17
|
+
issues: SeoUrlValidationIssue[];
|
|
18
|
+
}
|
|
19
|
+
export interface SeoOriginOptions {
|
|
20
|
+
allowSubdomains?: boolean;
|
|
21
|
+
protocols?: readonly ("http:" | "https:")[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SeoUrl } from "../core/types.js";
|
|
2
|
+
import { SeoTrailingSlash } from "../context/types.js";
|
|
3
|
+
export interface SeoUrlOptions {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
trailingSlash?: SeoTrailingSlash;
|
|
6
|
+
allowExternal?: boolean;
|
|
7
|
+
allowCredentials?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface SeoUrlValidationIssue {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SeoUrlResult {
|
|
15
|
+
url?: SeoUrl;
|
|
16
|
+
valid: boolean;
|
|
17
|
+
issues: SeoUrlValidationIssue[];
|
|
18
|
+
}
|
|
19
|
+
export interface SeoOriginOptions {
|
|
20
|
+
allowSubdomains?: boolean;
|
|
21
|
+
protocols?: readonly ("http:" | "https:")[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_validate = require("./validate.cjs");
|
|
3
|
+
exports.validateSeo = require_validate.validateSeo;
|
|
4
|
+
exports.validateSeoMetadata = require_validate.validateSeoMetadata;
|
|
5
|
+
exports.validateSeoNode = require_validate.validateSeoNode;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SeoCustomValidator, SeoRichResultProfile, SeoValidationContext, SeoValidationInput, SeoValidationIssue, SeoValidationMode, SeoValidationOptions, SeoValidationReport, SeoValidationSource, SeoValidationSummary } from "./types.cjs";
|
|
2
|
+
import { validateSeo, validateSeoMetadata, validateSeoNode } from "./validate.cjs";
|
|
3
|
+
export { type SeoCustomValidator, type SeoRichResultProfile, type SeoValidationContext, type SeoValidationInput, type SeoValidationIssue, type SeoValidationMode, type SeoValidationOptions, type SeoValidationReport, type SeoValidationSource, type SeoValidationSummary, validateSeo, validateSeoMetadata, validateSeoNode };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SeoCustomValidator, SeoRichResultProfile, SeoValidationContext, SeoValidationInput, SeoValidationIssue, SeoValidationMode, SeoValidationOptions, SeoValidationReport, SeoValidationSource, SeoValidationSummary } from "./types.js";
|
|
2
|
+
import { validateSeo, validateSeoMetadata, validateSeoNode } from "./validate.js";
|
|
3
|
+
export { type SeoCustomValidator, type SeoRichResultProfile, type SeoValidationContext, type SeoValidationInput, type SeoValidationIssue, type SeoValidationMode, type SeoValidationOptions, type SeoValidationReport, type SeoValidationSource, type SeoValidationSummary, validateSeo, validateSeoMetadata, validateSeoNode };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateSeo, validateSeoMetadata, validateSeoNode } from "./validate.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SeoGraph, SeoNode, SeoValidationIssue as SeoValidationIssue$1 } from "../core/types.cjs";
|
|
2
|
+
import { ResolvedSeoMetadata, SeoPageMetadata } from "../metadata/types.cjs";
|
|
3
|
+
import { SchemaRegistry } from "../schema/types.cjs";
|
|
4
|
+
export type SeoValidationMode = "off" | "warn" | "strict";
|
|
5
|
+
export type SeoValidationSource = "graph" | "schema" | "semantic" | "metadata" | "rich-results" | "registry" | "custom";
|
|
6
|
+
export type SeoRichResultProfile = "article" | "product" | "breadcrumb" | "event" | "recipe" | "job-posting" | "local-business" | "software-application";
|
|
7
|
+
export interface SeoValidationIssue extends SeoValidationIssue$1 {
|
|
8
|
+
source: SeoValidationSource;
|
|
9
|
+
suggestion?: string;
|
|
10
|
+
docsUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SeoValidationSummary {
|
|
13
|
+
errors: number;
|
|
14
|
+
warnings: number;
|
|
15
|
+
infos: number;
|
|
16
|
+
total: number;
|
|
17
|
+
}
|
|
18
|
+
export interface SeoValidationReport {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
mode: SeoValidationMode;
|
|
21
|
+
issues: SeoValidationIssue[];
|
|
22
|
+
summary: SeoValidationSummary;
|
|
23
|
+
}
|
|
24
|
+
export interface SeoValidationContext {
|
|
25
|
+
graph?: SeoGraph;
|
|
26
|
+
metadata?: SeoPageMetadata | ResolvedSeoMetadata;
|
|
27
|
+
registry: SchemaRegistry;
|
|
28
|
+
mode: SeoValidationMode;
|
|
29
|
+
}
|
|
30
|
+
export type SeoCustomValidator = (context: SeoValidationContext) => readonly SeoValidationIssue[];
|
|
31
|
+
export interface SeoValidationOptions {
|
|
32
|
+
/**
|
|
33
|
+
* off: disable validation.
|
|
34
|
+
* warn: report issues without rejecting output.
|
|
35
|
+
* strict: treat warnings as errors.
|
|
36
|
+
*/
|
|
37
|
+
mode?: SeoValidationMode;
|
|
38
|
+
/**
|
|
39
|
+
* Schema.org registry.
|
|
40
|
+
*
|
|
41
|
+
* Defaults to the generated Puzzle registry.
|
|
42
|
+
*/
|
|
43
|
+
registry?: SchemaRegistry;
|
|
44
|
+
/**
|
|
45
|
+
* Additional Google Rich Results profiles to validate.
|
|
46
|
+
*/
|
|
47
|
+
profiles?: readonly SeoRichResultProfile[];
|
|
48
|
+
/**
|
|
49
|
+
* Custom validation rules.
|
|
50
|
+
*/
|
|
51
|
+
validators?: readonly SeoCustomValidator[];
|
|
52
|
+
/**
|
|
53
|
+
* Validate required Schema.org properties.
|
|
54
|
+
*/
|
|
55
|
+
validateRequiredProperties?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether references to external entities are permitted.
|
|
58
|
+
*/
|
|
59
|
+
allowExternalReferences?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Base URL used for resolving local references.
|
|
62
|
+
*/
|
|
63
|
+
baseUrl?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Whether unknown Schema.org properties are accepted.
|
|
66
|
+
*
|
|
67
|
+
* Defaults to true in warn mode and false in strict mode.
|
|
68
|
+
*/
|
|
69
|
+
allowUnknownProperties?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface SeoValidationInput {
|
|
72
|
+
graph?: SeoGraph;
|
|
73
|
+
nodes?: readonly SeoNode[];
|
|
74
|
+
metadata?: SeoPageMetadata | ResolvedSeoMetadata;
|
|
75
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SeoGraph, SeoNode, SeoValidationIssue as SeoValidationIssue$1 } from "../core/types.js";
|
|
2
|
+
import { ResolvedSeoMetadata, SeoPageMetadata } from "../metadata/types.js";
|
|
3
|
+
import { SchemaRegistry } from "../schema/types.js";
|
|
4
|
+
export type SeoValidationMode = "off" | "warn" | "strict";
|
|
5
|
+
export type SeoValidationSource = "graph" | "schema" | "semantic" | "metadata" | "rich-results" | "registry" | "custom";
|
|
6
|
+
export type SeoRichResultProfile = "article" | "product" | "breadcrumb" | "event" | "recipe" | "job-posting" | "local-business" | "software-application";
|
|
7
|
+
export interface SeoValidationIssue extends SeoValidationIssue$1 {
|
|
8
|
+
source: SeoValidationSource;
|
|
9
|
+
suggestion?: string;
|
|
10
|
+
docsUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SeoValidationSummary {
|
|
13
|
+
errors: number;
|
|
14
|
+
warnings: number;
|
|
15
|
+
infos: number;
|
|
16
|
+
total: number;
|
|
17
|
+
}
|
|
18
|
+
export interface SeoValidationReport {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
mode: SeoValidationMode;
|
|
21
|
+
issues: SeoValidationIssue[];
|
|
22
|
+
summary: SeoValidationSummary;
|
|
23
|
+
}
|
|
24
|
+
export interface SeoValidationContext {
|
|
25
|
+
graph?: SeoGraph;
|
|
26
|
+
metadata?: SeoPageMetadata | ResolvedSeoMetadata;
|
|
27
|
+
registry: SchemaRegistry;
|
|
28
|
+
mode: SeoValidationMode;
|
|
29
|
+
}
|
|
30
|
+
export type SeoCustomValidator = (context: SeoValidationContext) => readonly SeoValidationIssue[];
|
|
31
|
+
export interface SeoValidationOptions {
|
|
32
|
+
/**
|
|
33
|
+
* off: disable validation.
|
|
34
|
+
* warn: report issues without rejecting output.
|
|
35
|
+
* strict: treat warnings as errors.
|
|
36
|
+
*/
|
|
37
|
+
mode?: SeoValidationMode;
|
|
38
|
+
/**
|
|
39
|
+
* Schema.org registry.
|
|
40
|
+
*
|
|
41
|
+
* Defaults to the generated Puzzle registry.
|
|
42
|
+
*/
|
|
43
|
+
registry?: SchemaRegistry;
|
|
44
|
+
/**
|
|
45
|
+
* Additional Google Rich Results profiles to validate.
|
|
46
|
+
*/
|
|
47
|
+
profiles?: readonly SeoRichResultProfile[];
|
|
48
|
+
/**
|
|
49
|
+
* Custom validation rules.
|
|
50
|
+
*/
|
|
51
|
+
validators?: readonly SeoCustomValidator[];
|
|
52
|
+
/**
|
|
53
|
+
* Validate required Schema.org properties.
|
|
54
|
+
*/
|
|
55
|
+
validateRequiredProperties?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether references to external entities are permitted.
|
|
58
|
+
*/
|
|
59
|
+
allowExternalReferences?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Base URL used for resolving local references.
|
|
62
|
+
*/
|
|
63
|
+
baseUrl?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Whether unknown Schema.org properties are accepted.
|
|
66
|
+
*
|
|
67
|
+
* Defaults to true in warn mode and false in strict mode.
|
|
68
|
+
*/
|
|
69
|
+
allowUnknownProperties?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface SeoValidationInput {
|
|
72
|
+
graph?: SeoGraph;
|
|
73
|
+
nodes?: readonly SeoNode[];
|
|
74
|
+
metadata?: SeoPageMetadata | ResolvedSeoMetadata;
|
|
75
|
+
}
|