@ingram-tech/nk-seo 0.3.0 → 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 +123 -19
- package/dist/alternates.d.ts +41 -0
- package/dist/alternates.d.ts.map +1 -0
- package/dist/alternates.js +30 -0
- package/dist/alternates.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +1 -1
- package/dist/components.js.map +1 -1
- package/dist/hreflang.d.ts +10 -18
- package/dist/hreflang.d.ts.map +1 -1
- package/dist/hreflang.js +14 -14
- package/dist/hreflang.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/json-ld.d.ts +1 -3
- package/dist/json-ld.d.ts.map +1 -1
- package/dist/json-ld.js +17 -5
- package/dist/json-ld.js.map +1 -1
- package/dist/metadata.d.ts +25 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +24 -3
- package/dist/metadata.js.map +1 -1
- package/dist/og.d.ts +68 -0
- package/dist/og.d.ts.map +1 -0
- package/dist/og.js +62 -0
- package/dist/og.js.map +1 -0
- package/dist/routes.d.ts +91 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +78 -0
- package/dist/routes.js.map +1 -0
- package/dist/schema.d.ts +185 -27
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +36 -11
- package/dist/schema.js.map +1 -1
- package/dist/url.d.ts +8 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +8 -0
- package/dist/url.js.map +1 -0
- package/package.json +6 -1
package/dist/metadata.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/og.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ImageResponse } from "next/og";
|
|
2
|
+
type ImageResponseOptions = NonNullable<ConstructorParameters<typeof ImageResponse>[1]>;
|
|
3
|
+
/**
|
|
4
|
+
* A branded Open Graph / Twitter card built with `next/og`. Lives on its own
|
|
5
|
+
* entry (`@ingram-tech/nk-seo/og`) because it imports `next/og` (satori + the
|
|
6
|
+
* resvg wasm) — the package root and `/components` stay free of it.
|
|
7
|
+
*
|
|
8
|
+
* Satori gotcha this template encodes so you don't have to: **every element
|
|
9
|
+
* with more than one child must set `display: "flex"`**, and you may not mix a
|
|
10
|
+
* text node and an element as siblings. The layout below keeps each region a
|
|
11
|
+
* single text node, so titles stay plain strings and the accent is carried by
|
|
12
|
+
* the mark — never by a coloured `<span>` inside the headline.
|
|
13
|
+
*/
|
|
14
|
+
export interface OgImageOptions {
|
|
15
|
+
/** The headline. Kept a single text node (no inline markup) for Satori. */
|
|
16
|
+
title: string;
|
|
17
|
+
/** Secondary line under the title. */
|
|
18
|
+
subtitle?: string;
|
|
19
|
+
/** Small label above the title (e.g. a section or category). */
|
|
20
|
+
eyebrow?: string;
|
|
21
|
+
/** Bottom-left footer, e.g. the bare domain. */
|
|
22
|
+
footer?: string;
|
|
23
|
+
/** Brand name shown next to the mark, top-left. */
|
|
24
|
+
wordmark?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Logo image rendered as the top-left mark instead of the accent square.
|
|
27
|
+
* Absolute URL or data URI (Satori fetches it at render time).
|
|
28
|
+
*/
|
|
29
|
+
logo?: string;
|
|
30
|
+
/** Accent colour — the mark, and the eyebrow if present. Default indigo. */
|
|
31
|
+
accent?: string;
|
|
32
|
+
background?: string;
|
|
33
|
+
/** Primary text colour. */
|
|
34
|
+
ink?: string;
|
|
35
|
+
/** Secondary text colour (subtitle, footer). */
|
|
36
|
+
muted?: string;
|
|
37
|
+
size?: {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Custom fonts, passed through to `ImageResponse` (Satori accepts raw
|
|
43
|
+
* TTF/OTF/WOFF data). Pair with `fontFamily` to actually use them.
|
|
44
|
+
*/
|
|
45
|
+
fonts?: ImageResponseOptions["fonts"];
|
|
46
|
+
/** Font family for the whole card. Default "sans-serif". */
|
|
47
|
+
fontFamily?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns an `ImageResponse` for use as the body of an `opengraph-image` /
|
|
51
|
+
* `twitter-image` route.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // app/opengraph-image.tsx
|
|
55
|
+
* import { ogImageResponse } from "@ingram-tech/nk-seo/og";
|
|
56
|
+
* export const size = { width: 1200, height: 630 };
|
|
57
|
+
* export const contentType = "image/png";
|
|
58
|
+
* export default () =>
|
|
59
|
+
* ogImageResponse({
|
|
60
|
+
* title: "Ship faster with Acme",
|
|
61
|
+
* subtitle: "The all-in-one platform for modern teams.",
|
|
62
|
+
* wordmark: "Acme",
|
|
63
|
+
* footer: "example.com",
|
|
64
|
+
* });
|
|
65
|
+
*/
|
|
66
|
+
export declare function ogImageResponse(options: OgImageOptions): ImageResponse;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=og.d.ts.map
|
package/dist/og.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"og.d.ts","sourceRoot":"","sources":["../src/og.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,KAAK,oBAAoB,GAAG,WAAW,CAAC,qBAAqB,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAExF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC;;;OAGG;IACH,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACtC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAiFtE"}
|
package/dist/og.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ImageResponse } from "next/og";
|
|
3
|
+
const DEFAULTS = {
|
|
4
|
+
accent: "#565ac9",
|
|
5
|
+
background: "#ffffff",
|
|
6
|
+
ink: "#171717",
|
|
7
|
+
muted: "#8a8a8a",
|
|
8
|
+
size: { width: 1200, height: 630 },
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Returns an `ImageResponse` for use as the body of an `opengraph-image` /
|
|
12
|
+
* `twitter-image` route.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // app/opengraph-image.tsx
|
|
16
|
+
* import { ogImageResponse } from "@ingram-tech/nk-seo/og";
|
|
17
|
+
* export const size = { width: 1200, height: 630 };
|
|
18
|
+
* export const contentType = "image/png";
|
|
19
|
+
* export default () =>
|
|
20
|
+
* ogImageResponse({
|
|
21
|
+
* title: "Ship faster with Acme",
|
|
22
|
+
* subtitle: "The all-in-one platform for modern teams.",
|
|
23
|
+
* wordmark: "Acme",
|
|
24
|
+
* footer: "example.com",
|
|
25
|
+
* });
|
|
26
|
+
*/
|
|
27
|
+
export function ogImageResponse(options) {
|
|
28
|
+
const accent = options.accent ?? DEFAULTS.accent;
|
|
29
|
+
const background = options.background ?? DEFAULTS.background;
|
|
30
|
+
const ink = options.ink ?? DEFAULTS.ink;
|
|
31
|
+
const muted = options.muted ?? DEFAULTS.muted;
|
|
32
|
+
const size = options.size ?? DEFAULTS.size;
|
|
33
|
+
return new ImageResponse(_jsxs("div", { style: {
|
|
34
|
+
width: "100%",
|
|
35
|
+
height: "100%",
|
|
36
|
+
display: "flex",
|
|
37
|
+
flexDirection: "column",
|
|
38
|
+
justifyContent: "space-between",
|
|
39
|
+
background,
|
|
40
|
+
padding: "80px",
|
|
41
|
+
fontFamily: options.fontFamily ?? "sans-serif",
|
|
42
|
+
}, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: "20px" }, children: [options.logo ? (_jsx("img", { src: options.logo, width: 44, height: 44, alt: "" })) : (_jsx("div", { style: {
|
|
43
|
+
width: "44px",
|
|
44
|
+
height: "44px",
|
|
45
|
+
borderRadius: "12px",
|
|
46
|
+
background: accent,
|
|
47
|
+
} })), options.wordmark ? (_jsx("div", { style: { fontSize: "40px", fontWeight: 600, color: ink }, children: options.wordmark })) : null] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "24px" }, children: [options.eyebrow ? (_jsx("div", { style: {
|
|
48
|
+
fontSize: "28px",
|
|
49
|
+
fontWeight: 600,
|
|
50
|
+
letterSpacing: "0.04em",
|
|
51
|
+
textTransform: "uppercase",
|
|
52
|
+
color: accent,
|
|
53
|
+
}, children: options.eyebrow })) : null, _jsx("div", { style: {
|
|
54
|
+
fontSize: "76px",
|
|
55
|
+
fontWeight: 700,
|
|
56
|
+
lineHeight: 1.05,
|
|
57
|
+
letterSpacing: "-0.02em",
|
|
58
|
+
color: ink,
|
|
59
|
+
maxWidth: "1000px",
|
|
60
|
+
}, children: options.title }), options.subtitle ? (_jsx("div", { style: { fontSize: "34px", color: muted, maxWidth: "900px" }, children: options.subtitle })) : null] }), options.footer ? (_jsx("div", { style: { fontSize: "30px", color: muted }, children: options.footer })) : (_jsx("div", { style: { display: "flex" } }))] }), { ...size, ...(options.fonts ? { fonts: options.fonts } : {}) });
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=og.js.map
|
package/dist/og.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"og.js","sourceRoot":"","sources":["../src/og.tsx"],"names":[],"mappings":"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"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { MetadataRoute } from "next";
|
|
2
|
+
/**
|
|
3
|
+
* Route-handler helpers for `app/sitemap.ts` and `app/robots.ts`. Pure — no
|
|
4
|
+
* React — so they live in the package root next to the metadata factory and can
|
|
5
|
+
* be imported by route handlers freely.
|
|
6
|
+
*
|
|
7
|
+
* Both resolve site-relative paths against one `baseUrl` (pass your deployment's
|
|
8
|
+
* own origin, e.g. `getServerUrl()`), so preview / sandbox deployments advertise
|
|
9
|
+
* themselves and never leak the production host.
|
|
10
|
+
*/
|
|
11
|
+
type ChangeFrequency = NonNullable<MetadataRoute.Sitemap[number]["changeFrequency"]>;
|
|
12
|
+
/** hreflang → URL map, keyed by the locale codes Next accepts. */
|
|
13
|
+
export type SitemapLanguages = NonNullable<NonNullable<MetadataRoute.Sitemap[number]["alternates"]>["languages"]>;
|
|
14
|
+
/** A sitemap entry. A bare string is shorthand for `{ path }`. */
|
|
15
|
+
export interface SitemapRoute {
|
|
16
|
+
/** Site-relative path (e.g. "/pricing") or an absolute URL. */
|
|
17
|
+
path: string;
|
|
18
|
+
lastModified?: string | Date;
|
|
19
|
+
changeFrequency?: ChangeFrequency;
|
|
20
|
+
/** 0.0–1.0. Defaults to 1 for "/" and {@link SitemapConfig.defaultPriority} otherwise. */
|
|
21
|
+
priority?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Per-locale alternates of this route (hreflang → site-relative path or
|
|
24
|
+
* absolute URL), e.g. `{ en: "/about", fr: "/fr/about" }`. Pairs with
|
|
25
|
+
* `<HreflangLinks>` / `hreflangAlternates` on the page side.
|
|
26
|
+
*/
|
|
27
|
+
languages?: SitemapLanguages;
|
|
28
|
+
}
|
|
29
|
+
export interface SitemapConfig {
|
|
30
|
+
/** Absolute site origin, e.g. "https://example.com". */
|
|
31
|
+
baseUrl: string;
|
|
32
|
+
/** The indexable routes. Strings or `SitemapRoute` objects may be mixed. */
|
|
33
|
+
routes: Array<string | SitemapRoute>;
|
|
34
|
+
/** Applied when an entry omits `lastModified`. */
|
|
35
|
+
lastModified?: string | Date;
|
|
36
|
+
/** Applied when an entry omits `changeFrequency`. */
|
|
37
|
+
defaultChangeFrequency?: ChangeFrequency;
|
|
38
|
+
/** Priority for every non-root entry that omits its own. Default 0.7. */
|
|
39
|
+
defaultPriority?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds a `MetadataRoute.Sitemap` from a list of routes, resolving relative
|
|
43
|
+
* paths against `baseUrl`. `"/"` defaults to priority 1; every other route
|
|
44
|
+
* defaults to `defaultPriority` (0.7).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // app/sitemap.ts
|
|
48
|
+
* import { createSitemap } from "@ingram-tech/nk-seo";
|
|
49
|
+
* export default () =>
|
|
50
|
+
* createSitemap({
|
|
51
|
+
* baseUrl: getServerUrl(),
|
|
52
|
+
* routes: ["/", "/pricing", "/docs", "/faq"],
|
|
53
|
+
* });
|
|
54
|
+
*/
|
|
55
|
+
export declare function createSitemap(config: SitemapConfig): MetadataRoute.Sitemap;
|
|
56
|
+
export interface RobotsConfig {
|
|
57
|
+
/** Absolute site origin, e.g. "https://example.com". */
|
|
58
|
+
baseUrl: string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether this deployment is the canonical production host. When false, the
|
|
61
|
+
* whole site is disallowed so preview / sandbox / branch deployments never
|
|
62
|
+
* get indexed and dilute the real domain with duplicate content. Derive it
|
|
63
|
+
* from `VERCEL_ENV === "production"` (and/or a host check).
|
|
64
|
+
*/
|
|
65
|
+
isProduction: boolean;
|
|
66
|
+
/** Prod-only disallow list (private routes, APIs, auth flows). */
|
|
67
|
+
disallow?: string[];
|
|
68
|
+
/** Prod allow rule. Default "/". */
|
|
69
|
+
allow?: string | string[];
|
|
70
|
+
/** Sitemap path advertised in prod. Default "/sitemap.xml". Pass null to omit. */
|
|
71
|
+
sitemapPath?: string | null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Builds a `MetadataRoute.Robots`. On non-production hosts it returns a
|
|
75
|
+
* blanket `Disallow: /` — the single most-forgotten SEO safeguard on Vercel,
|
|
76
|
+
* where preview and branch URLs are otherwise crawlable and compete with the
|
|
77
|
+
* production domain.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // app/robots.ts
|
|
81
|
+
* import { createRobots } from "@ingram-tech/nk-seo";
|
|
82
|
+
* export default () =>
|
|
83
|
+
* createRobots({
|
|
84
|
+
* baseUrl: getServerUrl(),
|
|
85
|
+
* isProduction: process.env.VERCEL_ENV === "production",
|
|
86
|
+
* disallow: ["/api/", "/internal/", "/login"],
|
|
87
|
+
* });
|
|
88
|
+
*/
|
|
89
|
+
export declare function createRobots(config: RobotsConfig): MetadataRoute.Robots;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C;;;;;;;;GAQG;AAEH,KAAK,eAAe,GAAG,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAErF,kEAAkE;AAClE,MAAM,MAAM,gBAAgB,GAAG,WAAW,CACzC,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CACrE,CAAC;AAEF,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC5B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC7B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC;IACrC,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qDAAqD;IACrD,sBAAsB,CAAC,EAAE,eAAe,CAAC;IACzC,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAeD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAAC,OAAO,CA6B1E;AAED,MAAM,WAAW,YAAY;IAC5B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,MAAM,CAcvE"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { absoluteUrl } from "./url.js";
|
|
2
|
+
const resolveLanguages = (languages, baseUrl) =>
|
|
3
|
+
// Object.entries/fromEntries erase the hreflang key union; the keys are
|
|
4
|
+
// passed through unchanged (only values are resolved), so restoring it is safe.
|
|
5
|
+
Object.fromEntries(Object.entries(languages).map(([locale, path]) => [
|
|
6
|
+
locale,
|
|
7
|
+
path === undefined ? path : absoluteUrl(path, baseUrl),
|
|
8
|
+
]));
|
|
9
|
+
/**
|
|
10
|
+
* Builds a `MetadataRoute.Sitemap` from a list of routes, resolving relative
|
|
11
|
+
* paths against `baseUrl`. `"/"` defaults to priority 1; every other route
|
|
12
|
+
* defaults to `defaultPriority` (0.7).
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // app/sitemap.ts
|
|
16
|
+
* import { createSitemap } from "@ingram-tech/nk-seo";
|
|
17
|
+
* export default () =>
|
|
18
|
+
* createSitemap({
|
|
19
|
+
* baseUrl: getServerUrl(),
|
|
20
|
+
* routes: ["/", "/pricing", "/docs", "/faq"],
|
|
21
|
+
* });
|
|
22
|
+
*/
|
|
23
|
+
export function createSitemap(config) {
|
|
24
|
+
const defaultPriority = config.defaultPriority ?? 0.7;
|
|
25
|
+
return config.routes.map((entry) => {
|
|
26
|
+
const route = typeof entry === "string" ? { path: entry } : entry;
|
|
27
|
+
const lastModified = route.lastModified ?? config.lastModified;
|
|
28
|
+
const changeFrequency = route.changeFrequency ?? config.defaultChangeFrequency;
|
|
29
|
+
const priority = route.priority ?? (route.path === "/" ? 1 : defaultPriority);
|
|
30
|
+
if (priority < 0 || priority > 1) {
|
|
31
|
+
throw new RangeError(`createSitemap: priority must be between 0 and 1, got ${priority} for "${route.path}"`);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
url: absoluteUrl(route.path, config.baseUrl),
|
|
35
|
+
...(lastModified ? { lastModified } : {}),
|
|
36
|
+
...(changeFrequency ? { changeFrequency } : {}),
|
|
37
|
+
priority,
|
|
38
|
+
...(route.languages
|
|
39
|
+
? {
|
|
40
|
+
alternates: {
|
|
41
|
+
languages: resolveLanguages(route.languages, config.baseUrl),
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Builds a `MetadataRoute.Robots`. On non-production hosts it returns a
|
|
50
|
+
* blanket `Disallow: /` — the single most-forgotten SEO safeguard on Vercel,
|
|
51
|
+
* where preview and branch URLs are otherwise crawlable and compete with the
|
|
52
|
+
* production domain.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // app/robots.ts
|
|
56
|
+
* import { createRobots } from "@ingram-tech/nk-seo";
|
|
57
|
+
* export default () =>
|
|
58
|
+
* createRobots({
|
|
59
|
+
* baseUrl: getServerUrl(),
|
|
60
|
+
* isProduction: process.env.VERCEL_ENV === "production",
|
|
61
|
+
* disallow: ["/api/", "/internal/", "/login"],
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
export function createRobots(config) {
|
|
65
|
+
if (!config.isProduction) {
|
|
66
|
+
return { rules: { userAgent: "*", disallow: "/" } };
|
|
67
|
+
}
|
|
68
|
+
const sitemapPath = config.sitemapPath === undefined ? "/sitemap.xml" : config.sitemapPath;
|
|
69
|
+
return {
|
|
70
|
+
rules: {
|
|
71
|
+
userAgent: "*",
|
|
72
|
+
allow: config.allow ?? "/",
|
|
73
|
+
...(config.disallow?.length ? { disallow: config.disallow } : {}),
|
|
74
|
+
},
|
|
75
|
+
...(sitemapPath ? { sitemap: absoluteUrl(sitemapPath, config.baseUrl) } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,27 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typed schema.org JSON-LD builders. Each returns a plain object ready to hand
|
|
3
|
-
* to `<JsonLd>` (from "@ingram-tech/nk-seo/components").
|
|
4
|
-
*
|
|
5
|
-
* The standalone builders take values as-is: pass **absolute** URLs for
|
|
6
|
-
* `url`/`image`/`logo` fields. If your pages work in site-relative paths and
|
|
7
|
-
* share one Organization, prefer {@link createSeo}, which resolves paths against
|
|
8
|
-
* a base URL and injects the publisher/provider for you.
|
|
9
|
-
*/
|
|
10
|
-
/** A schema.org node. Hand it to `<JsonLd data={...} />`. */
|
|
1
|
+
/** A loosely-typed schema.org node. The builders return more precise subtypes. */
|
|
11
2
|
export type JsonLdNode = Record<string, unknown>;
|
|
3
|
+
/** A top-level schema.org node: the payload plus its `@context`. */
|
|
4
|
+
export type WithContext<T> = T & {
|
|
5
|
+
"@context": "https://schema.org";
|
|
6
|
+
};
|
|
12
7
|
export interface FaqItem {
|
|
13
8
|
question: string;
|
|
14
9
|
answer: string;
|
|
15
10
|
}
|
|
11
|
+
export type QuestionNode = {
|
|
12
|
+
"@type": "Question";
|
|
13
|
+
name: string;
|
|
14
|
+
acceptedAnswer: {
|
|
15
|
+
"@type": "Answer";
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export type FaqPageNode = {
|
|
20
|
+
"@type": "FAQPage";
|
|
21
|
+
mainEntity: QuestionNode[];
|
|
22
|
+
};
|
|
16
23
|
/** FAQPage: marks up a list of question/answer pairs for FAQ rich results. */
|
|
17
|
-
export declare function faqPage(items: FaqItem[]):
|
|
24
|
+
export declare function faqPage(items: FaqItem[]): WithContext<FaqPageNode>;
|
|
18
25
|
export interface BreadcrumbItem {
|
|
19
26
|
name: string;
|
|
20
27
|
/** Absolute URL of the crumb. */
|
|
21
28
|
url: string;
|
|
22
29
|
}
|
|
30
|
+
export type ListItemNode = {
|
|
31
|
+
"@type": "ListItem";
|
|
32
|
+
position: number;
|
|
33
|
+
name: string;
|
|
34
|
+
item: string;
|
|
35
|
+
};
|
|
36
|
+
export type BreadcrumbListNode = {
|
|
37
|
+
"@type": "BreadcrumbList";
|
|
38
|
+
itemListElement: ListItemNode[];
|
|
39
|
+
};
|
|
23
40
|
/** BreadcrumbList: the trail of links Google shows in place of the raw URL. */
|
|
24
|
-
export declare function breadcrumbList(items: BreadcrumbItem[]):
|
|
41
|
+
export declare function breadcrumbList(items: BreadcrumbItem[]): WithContext<BreadcrumbListNode>;
|
|
25
42
|
export interface OrganizationInput {
|
|
26
43
|
name: string;
|
|
27
44
|
/** Absolute URL of the organization's site. */
|
|
@@ -30,19 +47,46 @@ export interface OrganizationInput {
|
|
|
30
47
|
logo?: string;
|
|
31
48
|
description?: string;
|
|
32
49
|
email?: string;
|
|
50
|
+
telephone?: string;
|
|
51
|
+
/** Registered/office postal address. */
|
|
52
|
+
address?: PostalAddressInput;
|
|
33
53
|
/** Profile/social URLs that corroborate the entity (LinkedIn, Crunchbase…). */
|
|
34
54
|
sameAs?: string[];
|
|
35
55
|
}
|
|
56
|
+
export type PostalAddressNode = {
|
|
57
|
+
"@type": "PostalAddress";
|
|
58
|
+
} & PostalAddressInput;
|
|
59
|
+
export type ImageObjectNode = {
|
|
60
|
+
"@type": "ImageObject";
|
|
61
|
+
url: string;
|
|
62
|
+
};
|
|
63
|
+
export type OrganizationNode = {
|
|
64
|
+
"@type": "Organization";
|
|
65
|
+
name: string;
|
|
66
|
+
url?: string;
|
|
67
|
+
logo?: ImageObjectNode;
|
|
68
|
+
description?: string;
|
|
69
|
+
email?: string;
|
|
70
|
+
telephone?: string;
|
|
71
|
+
address?: PostalAddressNode;
|
|
72
|
+
sameAs?: string[];
|
|
73
|
+
};
|
|
36
74
|
/** Organization: the entity behind the site (for the knowledge panel). */
|
|
37
|
-
export declare function organization(input: OrganizationInput):
|
|
75
|
+
export declare function organization(input: OrganizationInput): WithContext<OrganizationNode>;
|
|
38
76
|
export interface WebsiteInput {
|
|
39
77
|
name: string;
|
|
40
78
|
/** Absolute URL of the site root. */
|
|
41
79
|
url: string;
|
|
42
80
|
publisher?: OrganizationInput;
|
|
43
81
|
}
|
|
82
|
+
export type WebsiteNode = {
|
|
83
|
+
"@type": "WebSite";
|
|
84
|
+
name: string;
|
|
85
|
+
url: string;
|
|
86
|
+
publisher?: OrganizationNode;
|
|
87
|
+
};
|
|
44
88
|
/** WebSite: identifies the site as an entity; pair with {@link organization}. */
|
|
45
|
-
export declare function website(input: WebsiteInput):
|
|
89
|
+
export declare function website(input: WebsiteInput): WithContext<WebsiteNode>;
|
|
46
90
|
export interface OfferInput {
|
|
47
91
|
priceCurrency: string;
|
|
48
92
|
/** Single price. Mutually exclusive with low/high (which emit an AggregateOffer). */
|
|
@@ -53,6 +97,20 @@ export interface OfferInput {
|
|
|
53
97
|
/** Absolute URL of the pricing/offer page. */
|
|
54
98
|
url?: string;
|
|
55
99
|
}
|
|
100
|
+
export type OfferNode = {
|
|
101
|
+
"@type": "Offer";
|
|
102
|
+
priceCurrency: string;
|
|
103
|
+
price?: string;
|
|
104
|
+
url?: string;
|
|
105
|
+
};
|
|
106
|
+
export type AggregateOfferNode = {
|
|
107
|
+
"@type": "AggregateOffer";
|
|
108
|
+
priceCurrency: string;
|
|
109
|
+
lowPrice?: string;
|
|
110
|
+
highPrice?: string;
|
|
111
|
+
offerCount?: string;
|
|
112
|
+
url?: string;
|
|
113
|
+
};
|
|
56
114
|
export interface SoftwareApplicationInput {
|
|
57
115
|
name: string;
|
|
58
116
|
description?: string;
|
|
@@ -64,8 +122,17 @@ export interface SoftwareApplicationInput {
|
|
|
64
122
|
operatingSystem?: string;
|
|
65
123
|
offers?: OfferInput;
|
|
66
124
|
}
|
|
125
|
+
export type SoftwareApplicationNode = {
|
|
126
|
+
"@type": "SoftwareApplication";
|
|
127
|
+
name: string;
|
|
128
|
+
applicationCategory?: string;
|
|
129
|
+
operatingSystem?: string;
|
|
130
|
+
url?: string;
|
|
131
|
+
description?: string;
|
|
132
|
+
offers?: OfferNode | AggregateOfferNode;
|
|
133
|
+
};
|
|
67
134
|
/** SoftwareApplication: marks a product page as an app for rich results. */
|
|
68
|
-
export declare function softwareApplication(input: SoftwareApplicationInput):
|
|
135
|
+
export declare function softwareApplication(input: SoftwareApplicationInput): WithContext<SoftwareApplicationNode>;
|
|
69
136
|
export interface ArticleAuthor {
|
|
70
137
|
name: string;
|
|
71
138
|
/** Absolute URL of the author's profile/page. */
|
|
@@ -86,8 +153,26 @@ export interface ArticleInput {
|
|
|
86
153
|
keywords?: string[];
|
|
87
154
|
publisher?: OrganizationInput;
|
|
88
155
|
}
|
|
156
|
+
export type ArticleAuthorNode = {
|
|
157
|
+
"@type": "Person";
|
|
158
|
+
name: string;
|
|
159
|
+
url?: string;
|
|
160
|
+
};
|
|
161
|
+
export type ArticleNode = {
|
|
162
|
+
"@type": "Article" | "BlogPosting" | "NewsArticle" | "ScholarlyArticle";
|
|
163
|
+
headline: string;
|
|
164
|
+
url: string;
|
|
165
|
+
mainEntityOfPage: string;
|
|
166
|
+
description?: string;
|
|
167
|
+
author?: ArticleAuthorNode[];
|
|
168
|
+
publisher?: OrganizationNode;
|
|
169
|
+
datePublished?: string;
|
|
170
|
+
dateModified?: string;
|
|
171
|
+
image?: string;
|
|
172
|
+
keywords?: string[];
|
|
173
|
+
};
|
|
89
174
|
/** Article (or BlogPosting/NewsArticle/ScholarlyArticle) for editorial pages. */
|
|
90
|
-
export declare function article(input: ArticleInput):
|
|
175
|
+
export declare function article(input: ArticleInput): WithContext<ArticleNode>;
|
|
91
176
|
export interface PersonInput {
|
|
92
177
|
name: string;
|
|
93
178
|
/** Absolute URL of the person's page/profile. */
|
|
@@ -104,8 +189,23 @@ export interface PersonInput {
|
|
|
104
189
|
/** Home location place name, e.g. "Brussels, Belgium". */
|
|
105
190
|
homeLocation?: string;
|
|
106
191
|
}
|
|
192
|
+
export type PersonNode = {
|
|
193
|
+
"@type": "Person";
|
|
194
|
+
name: string;
|
|
195
|
+
url?: string;
|
|
196
|
+
jobTitle?: string;
|
|
197
|
+
worksFor?: OrganizationNode;
|
|
198
|
+
image?: string;
|
|
199
|
+
description?: string;
|
|
200
|
+
email?: string;
|
|
201
|
+
sameAs?: string[];
|
|
202
|
+
homeLocation?: {
|
|
203
|
+
"@type": "Place";
|
|
204
|
+
name: string;
|
|
205
|
+
};
|
|
206
|
+
};
|
|
107
207
|
/** Person: identifies an individual as an entity (portfolio, team, author bios). */
|
|
108
|
-
export declare function person(input: PersonInput):
|
|
208
|
+
export declare function person(input: PersonInput): WithContext<PersonNode>;
|
|
109
209
|
export interface PostalAddressInput {
|
|
110
210
|
streetAddress?: string;
|
|
111
211
|
addressLocality?: string;
|
|
@@ -145,8 +245,36 @@ export interface LocalBusinessInput {
|
|
|
145
245
|
/** schema.org LocalBusiness subtype, e.g. "Store", "ArtGallery". Default "LocalBusiness". */
|
|
146
246
|
type?: string;
|
|
147
247
|
}
|
|
248
|
+
export type GeoCoordinatesNode = {
|
|
249
|
+
"@type": "GeoCoordinates";
|
|
250
|
+
latitude: number;
|
|
251
|
+
longitude: number;
|
|
252
|
+
};
|
|
253
|
+
export type AggregateRatingNode = {
|
|
254
|
+
"@type": "AggregateRating";
|
|
255
|
+
ratingValue: string;
|
|
256
|
+
reviewCount: string;
|
|
257
|
+
};
|
|
258
|
+
export type LocalBusinessNode = {
|
|
259
|
+
/** "LocalBusiness" or the subtype passed as `type`. */
|
|
260
|
+
"@type": string;
|
|
261
|
+
name: string;
|
|
262
|
+
url?: string;
|
|
263
|
+
description?: string;
|
|
264
|
+
image?: string[];
|
|
265
|
+
logo?: string;
|
|
266
|
+
email?: string;
|
|
267
|
+
telephone?: string;
|
|
268
|
+
priceRange?: string;
|
|
269
|
+
openingHours?: string | string[];
|
|
270
|
+
address?: PostalAddressNode;
|
|
271
|
+
geo?: GeoCoordinatesNode;
|
|
272
|
+
hasMap?: string;
|
|
273
|
+
aggregateRating?: AggregateRatingNode;
|
|
274
|
+
sameAs?: string[];
|
|
275
|
+
};
|
|
148
276
|
/** LocalBusiness (or a subtype): a physical business for local/maps SEO. */
|
|
149
|
-
export declare function localBusiness(input: LocalBusinessInput):
|
|
277
|
+
export declare function localBusiness(input: LocalBusinessInput): WithContext<LocalBusinessNode>;
|
|
150
278
|
export interface EventLocationInput {
|
|
151
279
|
name?: string;
|
|
152
280
|
/** Postal address string or a URL for online events. */
|
|
@@ -174,12 +302,40 @@ export interface EventInput {
|
|
|
174
302
|
eventStatus?: string;
|
|
175
303
|
attendanceMode?: "offline" | "online" | "mixed";
|
|
176
304
|
}
|
|
305
|
+
export type PlaceNode = {
|
|
306
|
+
"@type": "Place";
|
|
307
|
+
name?: string;
|
|
308
|
+
address?: string;
|
|
309
|
+
url?: string;
|
|
310
|
+
};
|
|
311
|
+
export type VirtualLocationNode = {
|
|
312
|
+
"@type": "VirtualLocation";
|
|
313
|
+
url?: string;
|
|
314
|
+
};
|
|
315
|
+
export type EventNode = {
|
|
316
|
+
"@type": "Event";
|
|
317
|
+
name: string;
|
|
318
|
+
startDate: string;
|
|
319
|
+
endDate: string;
|
|
320
|
+
eventStatus: string;
|
|
321
|
+
eventAttendanceMode?: string;
|
|
322
|
+
description?: string;
|
|
323
|
+
url?: string;
|
|
324
|
+
image?: string[];
|
|
325
|
+
location?: PlaceNode | VirtualLocationNode;
|
|
326
|
+
organizer?: OrganizationNode;
|
|
327
|
+
offers?: OfferNode | AggregateOfferNode;
|
|
328
|
+
};
|
|
177
329
|
/** Event: a scheduled happening (summit, workshop, conference) for event rich results. */
|
|
178
|
-
export declare function event(input: EventInput):
|
|
330
|
+
export declare function event(input: EventInput): WithContext<EventNode>;
|
|
179
331
|
export interface SeoConfig {
|
|
180
|
-
/** Absolute site origin, e.g. "https://
|
|
332
|
+
/** Absolute site origin, e.g. "https://acme.example". */
|
|
181
333
|
baseUrl: string;
|
|
182
|
-
/**
|
|
334
|
+
/**
|
|
335
|
+
* Shared publisher/provider, injected into article()/website()/organization().
|
|
336
|
+
* Its `url` and `logo` may be site-relative — they are resolved against
|
|
337
|
+
* `baseUrl`.
|
|
338
|
+
*/
|
|
183
339
|
organization?: OrganizationInput;
|
|
184
340
|
}
|
|
185
341
|
/** A path-aware version of a crumb: `path` is resolved against the base URL. */
|
|
@@ -190,6 +346,8 @@ export interface BreadcrumbPath {
|
|
|
190
346
|
/**
|
|
191
347
|
* Binds the builders to a site so callers pass site-relative paths instead of
|
|
192
348
|
* absolute URLs, and the configured Organization is injected automatically.
|
|
349
|
+
* Nested URL fields (`organization.url`/`logo`, `offers.url`, article `image`)
|
|
350
|
+
* are resolved too — JSON-LD must never ship relative URLs.
|
|
193
351
|
*
|
|
194
352
|
* @example
|
|
195
353
|
* const seo = createSeo({ baseUrl: getServerUrl(), organization: ORG });
|
|
@@ -201,21 +359,21 @@ export declare function createSeo(config: SeoConfig): {
|
|
|
201
359
|
/** FAQPage (path-free). */
|
|
202
360
|
faqPage: typeof faqPage;
|
|
203
361
|
/** BreadcrumbList from site-relative paths. */
|
|
204
|
-
breadcrumbs(items: BreadcrumbPath[]):
|
|
362
|
+
breadcrumbs(items: BreadcrumbPath[]): WithContext<BreadcrumbListNode>;
|
|
205
363
|
/** Organization from config (with optional per-call overrides). */
|
|
206
|
-
organization(overrides?: Partial<OrganizationInput>):
|
|
364
|
+
organization(overrides?: Partial<OrganizationInput>): WithContext<OrganizationNode>;
|
|
207
365
|
/** WebSite from config, defaulting name/url/publisher to the configured org. */
|
|
208
|
-
website(input?: Partial<WebsiteInput>):
|
|
209
|
-
/** SoftwareApplication; `path` (default "/")
|
|
366
|
+
website(input?: Partial<WebsiteInput>): WithContext<WebsiteNode>;
|
|
367
|
+
/** SoftwareApplication; `path` (default "/") and `offers.url` are resolved. */
|
|
210
368
|
softwareApplication(input: Omit<SoftwareApplicationInput, "url"> & {
|
|
211
369
|
path?: string;
|
|
212
|
-
}):
|
|
370
|
+
}): WithContext<SoftwareApplicationNode>;
|
|
213
371
|
/** Article; `path`/`image` are resolved and the configured org becomes publisher. */
|
|
214
372
|
article(input: Omit<ArticleInput, "url" | "image" | "publisher"> & {
|
|
215
373
|
path: string;
|
|
216
374
|
image?: string;
|
|
217
375
|
publisher?: OrganizationInput;
|
|
218
|
-
}):
|
|
376
|
+
}): WithContext<ArticleNode>;
|
|
219
377
|
};
|
|
220
378
|
export type Seo = ReturnType<typeof createSeo>;
|
|
221
379
|
//# sourceMappingURL=schema.d.ts.map
|