@ingram-tech/nk-seo 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACtD,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CACzC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAE3E,OAAO,SAAS,YAAY,CAAC,KAAwB;QACpD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QACnD,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEjC,OAAO;YACN,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,UAAU,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE;YAC9B,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,SAAS,EAAE;gBACV,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,SAAS;gBAC7B,GAAG;gBACH,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/C,GAAG,CAAC,QAAQ;oBACX,CAAC,CAAC;wBACA,MAAM,EAAE;4BACP;gCACC,GAAG,EAAE,QAAQ;gCACb,KAAK,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI;gCACrC,MAAM,EAAE,IAAI,CAAC,kBAAkB,IAAI,GAAG;gCACtC,GAAG,EAAE,KAAK,CAAC,KAAK;6BAChB;yBACD;qBACD;oBACF,CAAC,CAAC,EAAE,CAAC;gBACN,GAAG,KAAK,CAAC,SAAS;aAClB;YACD,OAAO,EAAE;gBACR,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,GAAG,KAAK,CAAC,OAAO;aAChB;SACD,CAAC;IACH,CAAC,CAAC;AACH,CAAC"}
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
@@ -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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"}
@@ -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[]): JsonLdNode;
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[]): JsonLdNode;
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. */
@@ -36,16 +53,40 @@ export interface OrganizationInput {
36
53
  /** Profile/social URLs that corroborate the entity (LinkedIn, Crunchbase…). */
37
54
  sameAs?: string[];
38
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
+ };
39
74
  /** Organization: the entity behind the site (for the knowledge panel). */
40
- export declare function organization(input: OrganizationInput): JsonLdNode;
75
+ export declare function organization(input: OrganizationInput): WithContext<OrganizationNode>;
41
76
  export interface WebsiteInput {
42
77
  name: string;
43
78
  /** Absolute URL of the site root. */
44
79
  url: string;
45
80
  publisher?: OrganizationInput;
46
81
  }
82
+ export type WebsiteNode = {
83
+ "@type": "WebSite";
84
+ name: string;
85
+ url: string;
86
+ publisher?: OrganizationNode;
87
+ };
47
88
  /** WebSite: identifies the site as an entity; pair with {@link organization}. */
48
- export declare function website(input: WebsiteInput): JsonLdNode;
89
+ export declare function website(input: WebsiteInput): WithContext<WebsiteNode>;
49
90
  export interface OfferInput {
50
91
  priceCurrency: string;
51
92
  /** Single price. Mutually exclusive with low/high (which emit an AggregateOffer). */
@@ -56,6 +97,20 @@ export interface OfferInput {
56
97
  /** Absolute URL of the pricing/offer page. */
57
98
  url?: string;
58
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
+ };
59
114
  export interface SoftwareApplicationInput {
60
115
  name: string;
61
116
  description?: string;
@@ -67,8 +122,17 @@ export interface SoftwareApplicationInput {
67
122
  operatingSystem?: string;
68
123
  offers?: OfferInput;
69
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
+ };
70
134
  /** SoftwareApplication: marks a product page as an app for rich results. */
71
- export declare function softwareApplication(input: SoftwareApplicationInput): JsonLdNode;
135
+ export declare function softwareApplication(input: SoftwareApplicationInput): WithContext<SoftwareApplicationNode>;
72
136
  export interface ArticleAuthor {
73
137
  name: string;
74
138
  /** Absolute URL of the author's profile/page. */
@@ -89,8 +153,26 @@ export interface ArticleInput {
89
153
  keywords?: string[];
90
154
  publisher?: OrganizationInput;
91
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
+ };
92
174
  /** Article (or BlogPosting/NewsArticle/ScholarlyArticle) for editorial pages. */
93
- export declare function article(input: ArticleInput): JsonLdNode;
175
+ export declare function article(input: ArticleInput): WithContext<ArticleNode>;
94
176
  export interface PersonInput {
95
177
  name: string;
96
178
  /** Absolute URL of the person's page/profile. */
@@ -107,8 +189,23 @@ export interface PersonInput {
107
189
  /** Home location place name, e.g. "Brussels, Belgium". */
108
190
  homeLocation?: string;
109
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
+ };
110
207
  /** Person: identifies an individual as an entity (portfolio, team, author bios). */
111
- export declare function person(input: PersonInput): JsonLdNode;
208
+ export declare function person(input: PersonInput): WithContext<PersonNode>;
112
209
  export interface PostalAddressInput {
113
210
  streetAddress?: string;
114
211
  addressLocality?: string;
@@ -148,8 +245,36 @@ export interface LocalBusinessInput {
148
245
  /** schema.org LocalBusiness subtype, e.g. "Store", "ArtGallery". Default "LocalBusiness". */
149
246
  type?: string;
150
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
+ };
151
276
  /** LocalBusiness (or a subtype): a physical business for local/maps SEO. */
152
- export declare function localBusiness(input: LocalBusinessInput): JsonLdNode;
277
+ export declare function localBusiness(input: LocalBusinessInput): WithContext<LocalBusinessNode>;
153
278
  export interface EventLocationInput {
154
279
  name?: string;
155
280
  /** Postal address string or a URL for online events. */
@@ -177,12 +302,40 @@ export interface EventInput {
177
302
  eventStatus?: string;
178
303
  attendanceMode?: "offline" | "online" | "mixed";
179
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
+ };
180
329
  /** Event: a scheduled happening (summit, workshop, conference) for event rich results. */
181
- export declare function event(input: EventInput): JsonLdNode;
330
+ export declare function event(input: EventInput): WithContext<EventNode>;
182
331
  export interface SeoConfig {
183
- /** Absolute site origin, e.g. "https://financica.app". */
332
+ /** Absolute site origin, e.g. "https://acme.example". */
184
333
  baseUrl: string;
185
- /** Shared publisher/provider, injected into article()/website()/organization(). */
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
+ */
186
339
  organization?: OrganizationInput;
187
340
  }
188
341
  /** A path-aware version of a crumb: `path` is resolved against the base URL. */
@@ -193,6 +346,8 @@ export interface BreadcrumbPath {
193
346
  /**
194
347
  * Binds the builders to a site so callers pass site-relative paths instead of
195
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.
196
351
  *
197
352
  * @example
198
353
  * const seo = createSeo({ baseUrl: getServerUrl(), organization: ORG });
@@ -204,21 +359,21 @@ export declare function createSeo(config: SeoConfig): {
204
359
  /** FAQPage (path-free). */
205
360
  faqPage: typeof faqPage;
206
361
  /** BreadcrumbList from site-relative paths. */
207
- breadcrumbs(items: BreadcrumbPath[]): JsonLdNode;
362
+ breadcrumbs(items: BreadcrumbPath[]): WithContext<BreadcrumbListNode>;
208
363
  /** Organization from config (with optional per-call overrides). */
209
- organization(overrides?: Partial<OrganizationInput>): JsonLdNode;
364
+ organization(overrides?: Partial<OrganizationInput>): WithContext<OrganizationNode>;
210
365
  /** WebSite from config, defaulting name/url/publisher to the configured org. */
211
- website(input?: Partial<WebsiteInput>): JsonLdNode;
212
- /** SoftwareApplication; `path` (default "/") is resolved to an absolute URL. */
366
+ website(input?: Partial<WebsiteInput>): WithContext<WebsiteNode>;
367
+ /** SoftwareApplication; `path` (default "/") and `offers.url` are resolved. */
213
368
  softwareApplication(input: Omit<SoftwareApplicationInput, "url"> & {
214
369
  path?: string;
215
- }): JsonLdNode;
370
+ }): WithContext<SoftwareApplicationNode>;
216
371
  /** Article; `path`/`image` are resolved and the configured org becomes publisher. */
217
372
  article(input: Omit<ArticleInput, "url" | "image" | "publisher"> & {
218
373
  path: string;
219
374
  image?: string;
220
375
  publisher?: OrganizationInput;
221
- }): JsonLdNode;
376
+ }): WithContext<ArticleNode>;
222
377
  };
223
378
  export type Seo = ReturnType<typeof createSeo>;
224
379
  //# sourceMappingURL=schema.d.ts.map