@amjadiqbal/start-seo-kit 0.0.0-stage → 0.1.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.
@@ -0,0 +1,315 @@
1
+ import { t as jsonLd } from "./json-ld-BkYFVGHw.js";
2
+ // src/commerce.ts
3
+ const CONTEXT = "https://schema.org";
4
+ const SCHEMA = "https://schema.org/";
5
+ /** Product JSON-LD (with Offer or AggregateOffer, ratings, reviews, shipping and returns) for Google product rich results. */
6
+ function productJsonLd(p) {
7
+ return clean({
8
+ "@context": CONTEXT,
9
+ "@type": "Product",
10
+ "@id": p.id,
11
+ name: p.name,
12
+ description: p.description,
13
+ image: p.image,
14
+ url: p.url,
15
+ sku: p.sku,
16
+ mpn: p.mpn,
17
+ gtin: p.gtin,
18
+ brand: p.brand ? {
19
+ "@type": "Brand",
20
+ name: p.brand
21
+ } : void 0,
22
+ category: p.category,
23
+ color: p.color,
24
+ size: p.size,
25
+ material: p.material,
26
+ inProductGroupWithID: p.groupId,
27
+ offers: p.offers === void 0 ? void 0 : offersLd(p.offers),
28
+ aggregateRating: p.rating ? aggregateRatingLd(p.rating) : void 0,
29
+ review: p.reviews?.length ? p.reviews.map(reviewLd) : void 0
30
+ });
31
+ }
32
+ function offersLd(o) {
33
+ if (Array.isArray(o)) return o.map(offerLd);
34
+ return "lowPrice" in o ? aggregateOfferLd(o) : offerLd(o);
35
+ }
36
+ /** One `Offer`. */
37
+ function offerLd(o) {
38
+ const shipping = o.shipping === void 0 ? void 0 : Array.isArray(o.shipping) ? o.shipping : [o.shipping];
39
+ return clean({
40
+ "@type": "Offer",
41
+ price: o.price,
42
+ priceCurrency: o.currency,
43
+ availability: o.availability ? SCHEMA + o.availability : void 0,
44
+ itemCondition: o.condition ? SCHEMA + o.condition : void 0,
45
+ url: o.url,
46
+ priceValidUntil: dateOnly(o.priceValidUntil),
47
+ sku: o.sku,
48
+ seller: o.seller ? {
49
+ "@type": "Organization",
50
+ name: o.seller
51
+ } : void 0,
52
+ shippingDetails: shipping?.length ? shipping.map(shippingLd) : void 0,
53
+ hasMerchantReturnPolicy: o.returnPolicy ? returnPolicyLd(o.returnPolicy) : void 0
54
+ });
55
+ }
56
+ /** An `AggregateOffer` (price range across sellers or variants). */
57
+ function aggregateOfferLd(o) {
58
+ return clean({
59
+ "@type": "AggregateOffer",
60
+ lowPrice: o.lowPrice,
61
+ highPrice: o.highPrice,
62
+ priceCurrency: o.currency,
63
+ offerCount: o.offerCount,
64
+ availability: o.availability ? SCHEMA + o.availability : void 0
65
+ });
66
+ }
67
+ function aggregateRatingLd(r) {
68
+ return clean({
69
+ "@type": "AggregateRating",
70
+ ratingValue: r.value,
71
+ ratingCount: r.count,
72
+ reviewCount: r.reviewCount,
73
+ bestRating: r.best,
74
+ worstRating: r.worst
75
+ });
76
+ }
77
+ function reviewLd(r) {
78
+ const notes = (items) => items?.length ? {
79
+ "@type": "ItemList",
80
+ itemListElement: items.map((name, i) => ({
81
+ "@type": "ListItem",
82
+ position: i + 1,
83
+ name
84
+ }))
85
+ } : void 0;
86
+ return clean({
87
+ "@type": "Review",
88
+ author: {
89
+ "@type": "Person",
90
+ name: r.author
91
+ },
92
+ reviewRating: clean({
93
+ "@type": "Rating",
94
+ ratingValue: r.rating,
95
+ bestRating: r.best ?? 5,
96
+ worstRating: r.worst
97
+ }),
98
+ name: r.name,
99
+ reviewBody: r.body,
100
+ datePublished: dateOnly(r.datePublished),
101
+ positiveNotes: notes(r.positiveNotes),
102
+ negativeNotes: notes(r.negativeNotes)
103
+ });
104
+ }
105
+ /** `OfferShippingDetails`. */
106
+ function shippingLd(s) {
107
+ const countries = Array.isArray(s.country) ? s.country : [s.country];
108
+ const region = (country) => clean({
109
+ "@type": "DefinedRegion",
110
+ addressCountry: country,
111
+ addressRegion: s.regions
112
+ });
113
+ const range = (days) => days ? {
114
+ "@type": "QuantitativeValue",
115
+ minValue: days[0],
116
+ maxValue: days[1],
117
+ unitCode: "DAY"
118
+ } : void 0;
119
+ return clean({
120
+ "@type": "OfferShippingDetails",
121
+ shippingRate: {
122
+ "@type": "MonetaryAmount",
123
+ value: s.rate,
124
+ currency: s.currency
125
+ },
126
+ shippingDestination: countries.length === 1 ? region(countries[0]) : countries.map(region),
127
+ deliveryTime: s.handlingDays || s.transitDays ? clean({
128
+ "@type": "ShippingDeliveryTime",
129
+ handlingTime: range(s.handlingDays),
130
+ transitTime: range(s.transitDays)
131
+ }) : void 0
132
+ });
133
+ }
134
+ /** `MerchantReturnPolicy`. Also usable on an Organization (`hasMerchantReturnPolicy`). */
135
+ function returnPolicyLd(r) {
136
+ if (!("country" in r)) return {
137
+ "@type": "MerchantReturnPolicy",
138
+ merchantReturnLink: r.url
139
+ };
140
+ const category = {
141
+ finite: "MerchantReturnFiniteReturnWindow",
142
+ unlimited: "MerchantReturnUnlimitedWindow",
143
+ notPermitted: "MerchantReturnNotPermitted"
144
+ }[r.category];
145
+ const fees = r.fees === void 0 ? {} : r.fees === "free" ? { returnFees: `${SCHEMA}FreeReturn` } : r.fees === "customer" ? { returnFees: `${SCHEMA}ReturnFeesCustomerResponsibility` } : {
146
+ returnFees: `${SCHEMA}ReturnShippingFees`,
147
+ returnShippingFeesAmount: {
148
+ "@type": "MonetaryAmount",
149
+ value: r.fees.amount,
150
+ currency: r.fees.currency
151
+ }
152
+ };
153
+ const methods = {
154
+ mail: "ReturnByMail",
155
+ store: "ReturnInStore",
156
+ kiosk: "ReturnAtKiosk"
157
+ };
158
+ return clean({
159
+ "@type": "MerchantReturnPolicy",
160
+ applicableCountry: r.country,
161
+ returnPolicyCategory: SCHEMA + category,
162
+ merchantReturnDays: r.category === "finite" ? r.days : void 0,
163
+ returnMethod: r.method?.length ? r.method.map((m) => SCHEMA + methods[m]) : void 0,
164
+ merchantReturnLink: r.url,
165
+ ...fees
166
+ });
167
+ }
168
+ const OG_AVAILABILITY = {
169
+ InStock: "in stock",
170
+ OutOfStock: "out of stock",
171
+ PreOrder: "preorder",
172
+ PreSale: "preorder",
173
+ BackOrder: "available for order",
174
+ Discontinued: "discontinued",
175
+ LimitedAvailability: "in stock",
176
+ InStoreOnly: "in stock",
177
+ OnlineOnly: "in stock",
178
+ SoldOut: "out of stock"
179
+ };
180
+ const OG_CONDITION = {
181
+ NewCondition: "new",
182
+ UsedCondition: "used",
183
+ RefurbishedCondition: "refurbished",
184
+ DamagedCondition: "used"
185
+ };
186
+ /** Open Graph product tags: `product:price:amount`, `product:price:currency`, availability, condition, brand, item id. */
187
+ function productOpenGraph(p) {
188
+ const meta = [];
189
+ const first = p.offers === void 0 ? void 0 : Array.isArray(p.offers) ? p.offers[0] : p.offers;
190
+ if (first) {
191
+ const amount = "lowPrice" in first ? first.lowPrice : first.price;
192
+ meta.push({
193
+ property: "product:price:amount",
194
+ content: String(amount)
195
+ });
196
+ meta.push({
197
+ property: "product:price:currency",
198
+ content: first.currency
199
+ });
200
+ if (first.availability) meta.push({
201
+ property: "product:availability",
202
+ content: OG_AVAILABILITY[first.availability]
203
+ });
204
+ if ("condition" in first && first.condition) meta.push({
205
+ property: "product:condition",
206
+ content: OG_CONDITION[first.condition]
207
+ });
208
+ }
209
+ if (p.brand) meta.push({
210
+ property: "product:brand",
211
+ content: p.brand
212
+ });
213
+ if (p.sku) meta.push({
214
+ property: "product:retailer_item_id",
215
+ content: p.sku
216
+ });
217
+ if (p.groupId) meta.push({
218
+ property: "product:item_group_id",
219
+ content: p.groupId
220
+ });
221
+ return meta;
222
+ }
223
+ /**
224
+ * Everything a product page needs, as `seo()` options: `og:type` product, the product Open Graph tags
225
+ * and the Product JSON-LD. Spread it into your call:
226
+ *
227
+ * ```ts
228
+ * head: ({ loaderData: p }) => seo({ title: p.name, path: `/p/${p.slug}`, image: p.images[0], ...productSeo(p) })
229
+ * ```
230
+ */
231
+ function productSeo(p) {
232
+ return {
233
+ type: "product",
234
+ meta: productOpenGraph(p),
235
+ jsonLd: productJsonLd(p)
236
+ };
237
+ }
238
+ /**
239
+ * Check a product against Google's product snippet and merchant listing requirements
240
+ * (as documented on 2026-10-22). Use it in tests or development; it does not throw.
241
+ */
242
+ function checkProduct(p, options = {}) {
243
+ const errors = [];
244
+ const warnings = [];
245
+ const offers = p.offers === void 0 ? [] : Array.isArray(p.offers) ? p.offers : [p.offers];
246
+ if (!p.name?.trim()) errors.push("name is required");
247
+ if (!offers.length && !p.rating && !p.reviews?.length) errors.push("one of offers, rating or reviews is required for product snippets");
248
+ if (options.merchantListing) {
249
+ if (!p.image || Array.isArray(p.image) && !p.image.length) errors.push("image is required for merchant listings");
250
+ if (!offers.length) errors.push("offers is required for merchant listings");
251
+ }
252
+ if (!p.offers && (p.rating || p.reviews?.length)) warnings.push("offers is recommended: the Rich Results Test warns without it");
253
+ offers.forEach((o, i) => {
254
+ const at = offers.length > 1 ? `offers[${i}]` : "offers";
255
+ if ("lowPrice" in o) {
256
+ checkPrice(o.lowPrice, `${at}.lowPrice`, errors);
257
+ if (o.highPrice !== void 0) checkPrice(o.highPrice, `${at}.highPrice`, errors);
258
+ if (options.merchantListing) warnings.push(`${at}: use Offer rather than AggregateOffer for merchant listings and price-drop results`);
259
+ } else {
260
+ checkPrice(o.price, `${at}.price`, errors);
261
+ if (!o.availability) warnings.push(`${at}.availability is recommended`);
262
+ (o.shipping === void 0 ? [] : Array.isArray(o.shipping) ? o.shipping : [o.shipping]).forEach((s, j) => {
263
+ if (s.currency !== o.currency) errors.push(`${at}.shipping[${j}].currency must match the offer currency (${o.currency})`);
264
+ if (!s.handlingDays && !s.transitDays) errors.push(`${at}.shipping[${j}] needs handlingDays or transitDays (deliveryTime)`);
265
+ checkPrice(s.rate, `${at}.shipping[${j}].rate`, errors);
266
+ });
267
+ if (o.returnPolicy && "country" in o.returnPolicy && o.returnPolicy.category === "finite" && !o.returnPolicy.days) errors.push(`${at}.returnPolicy.days is required for a finite return window`);
268
+ }
269
+ if (!/^[A-Z]{3}$/.test(o.currency)) errors.push(`${at}.currency must be a 3-letter ISO 4217 code`);
270
+ });
271
+ if (p.rating) {
272
+ if (p.rating.count === void 0 && p.rating.reviewCount === void 0) errors.push("rating needs count or reviewCount");
273
+ const best = p.rating.best ?? 5;
274
+ if (p.rating.value > best || p.rating.value < (p.rating.worst ?? 1)) errors.push("rating.value is outside best/worst");
275
+ }
276
+ p.reviews?.forEach((r, i) => {
277
+ if (!r.author.trim()) errors.push(`reviews[${i}].author is required`);
278
+ if ((r.positiveNotes?.length ?? 0) + (r.negativeNotes?.length ?? 0) === 1) warnings.push(`reviews[${i}]: pros and cons need at least two statements in total`);
279
+ });
280
+ if (!p.brand) warnings.push("brand is recommended");
281
+ if (!p.gtin && !p.mpn && !p.sku) warnings.push("a gtin, mpn or sku is recommended");
282
+ if (!p.description) warnings.push("description is recommended");
283
+ return {
284
+ errors,
285
+ warnings
286
+ };
287
+ }
288
+ /** Map your products to sitemap entries (with image entries) for a `createSitemap` source. */
289
+ function productSitemapEntries(items, options) {
290
+ const out = [];
291
+ for (const item of items) {
292
+ const lastmod = options.lastmod?.(item);
293
+ const images = options.images?.(item);
294
+ out.push(clean({
295
+ loc: options.path(item),
296
+ lastmod,
297
+ images: images?.length ? images : void 0,
298
+ changefreq: options.changefreq,
299
+ priority: options.priority
300
+ }));
301
+ }
302
+ return out;
303
+ }
304
+ function checkPrice(value, at, errors) {
305
+ if (!(typeof value === "number" ? Number.isFinite(value) && value >= 0 : /^\d+(\.\d+)?$/.test(value))) errors.push(`${at} must be a non-negative number without currency symbols or separators`);
306
+ }
307
+ function dateOnly(value) {
308
+ return value instanceof Date ? value.toISOString().slice(0, 10) : value;
309
+ }
310
+ function clean(obj) {
311
+ const out = {};
312
+ for (const [k, v] of Object.entries(obj)) if (v !== void 0) out[k] = v;
313
+ return out;
314
+ }
315
+ export { aggregateOfferLd, aggregateRatingLd, checkProduct, jsonLd, offerLd, productJsonLd, productOpenGraph, productSeo, productSitemapEntries, returnPolicyLd, reviewLd, shippingLd };
@@ -0,0 +1,218 @@
1
+ import { g as TrailingSlash } from "./sitemap-DbEixSCi.js";
2
+ /** Any JSON value allowed inside a JSON-LD document. */
3
+ type JsonLdValue = string | number | boolean | null | JsonLdValue[] | {
4
+ [key: string]: JsonLdValue | undefined;
5
+ };
6
+ /** A JSON-LD node: an object with `@type` (and usually `@context`). */
7
+ interface JsonLdObject {
8
+ '@context'?: string | Record<string, JsonLdValue>;
9
+ '@type'?: string | string[];
10
+ '@id'?: string;
11
+ '@graph'?: JsonLdObject[];
12
+ [key: string]: JsonLdValue | JsonLdObject | JsonLdObject[] | undefined;
13
+ }
14
+ type Ref = string | JsonLdObject;
15
+ interface PersonInput {
16
+ name: string;
17
+ url?: string;
18
+ image?: string;
19
+ jobTitle?: string;
20
+ sameAs?: string[];
21
+ id?: string;
22
+ }
23
+ interface OrganizationInput {
24
+ name: string;
25
+ url?: string;
26
+ logo?: string;
27
+ sameAs?: string[];
28
+ email?: string;
29
+ telephone?: string;
30
+ id?: string;
31
+ /** `Organization` (default) or a subtype such as `Corporation` or `LocalBusiness`. */
32
+ type?: string;
33
+ }
34
+ interface WebSiteInput {
35
+ name: string;
36
+ url: string;
37
+ /** Enables the sitelinks search box: a URL with `{search_term_string}`, e.g. `https://acme.dev/search?q={search_term_string}`. */
38
+ searchUrlTemplate?: string;
39
+ publisher?: Ref;
40
+ inLanguage?: string;
41
+ id?: string;
42
+ }
43
+ interface ArticleLdInput {
44
+ /** `Article` (default), `BlogPosting`, `NewsArticle` or `TechArticle`. */
45
+ type?: 'Article' | 'BlogPosting' | 'NewsArticle' | 'TechArticle';
46
+ headline: string;
47
+ description?: string;
48
+ url?: string;
49
+ image?: string | string[];
50
+ datePublished?: string | Date;
51
+ dateModified?: string | Date;
52
+ author?: Ref | Ref[];
53
+ publisher?: Ref;
54
+ keywords?: string[];
55
+ id?: string;
56
+ }
57
+ interface BreadcrumbItem {
58
+ name: string;
59
+ /** Absolute URL. Leave it out on the last (current) item if you like. */
60
+ url?: string;
61
+ }
62
+ interface FaqItem {
63
+ question: string;
64
+ /** Plain text or simple HTML. */
65
+ answer: string;
66
+ }
67
+ /**
68
+ * Typed builders for common schema.org types. Each returns a JSON-LD object with `@context`.
69
+ * Product, Offer, Review and return/shipping policies live in `@amjadiqbal/start-seo-kit/commerce`.
70
+ */
71
+ declare const jsonLd: {
72
+ person(p: PersonInput): JsonLdObject;
73
+ organization(o: OrganizationInput): JsonLdObject;
74
+ website(w: WebSiteInput): JsonLdObject;
75
+ article(a: ArticleLdInput): JsonLdObject;
76
+ breadcrumbs(items: BreadcrumbItem[]): JsonLdObject;
77
+ faq(items: FaqItem[]): JsonLdObject;
78
+ /** Combine several nodes into one `@graph` document (their own `@context` is dropped). */
79
+ graph(...nodes: JsonLdObject[]): JsonLdObject;
80
+ };
81
+ /**
82
+ * One entry of a route's `head().meta` array, in the shapes TanStack Router renders.
83
+ * The JSON-LD shape carries an optional `content?: undefined` so it stays assignable to
84
+ * React's `<meta>` props type, which Router's React adapter uses for `meta`.
85
+ */
86
+ type MetaTag = {
87
+ title: string;
88
+ } | {
89
+ name: string;
90
+ content: string;
91
+ } | {
92
+ property: string;
93
+ content: string;
94
+ } | {
95
+ 'script:ld+json': JsonLdObject;
96
+ content?: undefined;
97
+ };
98
+ /** One entry of a route's `head().links` array. */
99
+ type LinkTag = {
100
+ rel: string;
101
+ href: string;
102
+ hrefLang?: string;
103
+ type?: string;
104
+ title?: string;
105
+ };
106
+ interface HeadTags {
107
+ meta: MetaTag[];
108
+ links: LinkTag[];
109
+ }
110
+ type ImageInput = string | {
111
+ url: string;
112
+ width?: number;
113
+ height?: number;
114
+ alt?: string;
115
+ type?: string;
116
+ };
117
+ type RobotsInput = string | {
118
+ index?: boolean;
119
+ follow?: boolean;
120
+ noarchive?: boolean;
121
+ nosnippet?: boolean;
122
+ noimageindex?: boolean;
123
+ notranslate?: boolean;
124
+ maxSnippet?: number;
125
+ maxImagePreview?: 'none' | 'standard' | 'large';
126
+ maxVideoPreview?: number;
127
+ unavailableAfter?: string | Date;
128
+ };
129
+ type TwitterCard = 'summary' | 'summary_large_image' | 'app' | 'player';
130
+ interface TwitterInput {
131
+ card?: TwitterCard;
132
+ /** `@handle` of the site. */
133
+ site?: string;
134
+ /** `@handle` of the author. */
135
+ creator?: string;
136
+ }
137
+ interface ArticleInput {
138
+ publishedTime?: string | Date;
139
+ modifiedTime?: string | Date;
140
+ expirationTime?: string | Date;
141
+ /** Author profile URL or name. Router keeps one tag per property, so only one author is emitted. */
142
+ author?: string;
143
+ section?: string;
144
+ }
145
+ interface SiteConfig {
146
+ /** Absolute origin (plus optional base path) of the site, e.g. `https://example.com`. */
147
+ url: string;
148
+ /** Site name, used for `og:site_name`. */
149
+ name?: string;
150
+ /** Title template; `%s` is replaced by the page title, e.g. `%s | Acme`. */
151
+ titleTemplate?: string;
152
+ /** Title used when a route passes none (not templated). */
153
+ defaultTitle?: string;
154
+ description?: string;
155
+ /** Default share image. Relative paths are resolved against `url`. */
156
+ image?: ImageInput;
157
+ /** `og:locale`, e.g. `en_US`. */
158
+ locale?: string;
159
+ twitter?: TwitterInput;
160
+ robots?: RobotsInput;
161
+ /** Match your router's `trailingSlash` option. Default `never`. */
162
+ trailingSlash?: TrailingSlash;
163
+ }
164
+ interface SeoInput {
165
+ title?: string;
166
+ /** Override the site template for this page, or `false` to use the title as is. */
167
+ titleTemplate?: string | false;
168
+ description?: string;
169
+ /** Path of this page (e.g. `/posts/${params.slug}`): sets the canonical link and `og:url`. */
170
+ path?: string;
171
+ /** Absolute or relative canonical URL, or `false` for none. Defaults to `path`. */
172
+ canonical?: string | false;
173
+ /** Share image, or `false` to drop the site default. */
174
+ image?: ImageInput | false;
175
+ /** `og:type`. Default `website`, or `article` when `article` is set. */
176
+ type?: string;
177
+ article?: ArticleInput;
178
+ robots?: RobotsInput;
179
+ /** Shorthand for `robots: { index: false, follow: true }`. */
180
+ noindex?: boolean;
181
+ /** hreflang alternates: `{ en: '/en/about', de: '/de/about', 'x-default': '/about' }`. */
182
+ alternates?: Record<string, string>;
183
+ locale?: string;
184
+ twitter?: TwitterInput;
185
+ /** Title for `og:title`/`twitter:title`. Default: the page title without the template. */
186
+ socialTitle?: string;
187
+ /** One or more JSON-LD objects (see the `jsonLd` helpers). */
188
+ jsonLd?: JsonLdObject | JsonLdObject[];
189
+ /** Extra meta tags, appended as given. */
190
+ meta?: MetaTag[];
191
+ /** Extra link tags, appended as given. */
192
+ links?: LinkTag[];
193
+ }
194
+ interface Seo {
195
+ /** Build the `meta` and `links` for a route's `head()`. */
196
+ (input?: SeoInput): HeadTags;
197
+ /** The site config with a normalized `url`. */
198
+ readonly site: Readonly<SiteConfig>;
199
+ /** Absolute URL of a page path, with the site's trailing-slash rule. */
200
+ url(path: string): string;
201
+ }
202
+ /**
203
+ * Create a typed head builder bound to your site's defaults.
204
+ *
205
+ * ```ts
206
+ * export const seo = defineSeo({ url: 'https://acme.dev', name: 'Acme', titleTemplate: '%s | Acme' })
207
+ * // in a route
208
+ * head: ({ loaderData }) => seo({ title: loaderData.post.title, path: `/posts/${loaderData.post.slug}` })
209
+ * ```
210
+ */
211
+ declare function defineSeo(config: SiteConfig): Seo;
212
+ /** Build head tags without a site config. Relative URLs are not allowed here. */
213
+ declare function seo(input: SeoInput & {
214
+ siteUrl?: string;
215
+ }): HeadTags;
216
+ /** Serialize robots directives, e.g. `{ index: false, maxImagePreview: 'large' }` -> `noindex, follow, max-image-preview:large`. */
217
+ declare function robotsContent(robots: RobotsInput | undefined): string | undefined;
218
+ export { jsonLd as C, WebSiteInput as S, FaqItem as _, MetaTag as a, OrganizationInput as b, SeoInput as c, TwitterInput as d, defineSeo as f, BreadcrumbItem as g, ArticleLdInput as h, LinkTag as i, SiteConfig as l, seo as m, HeadTags as n, RobotsInput as o, robotsContent as p, ImageInput as r, Seo as s, ArticleInput as t, TwitterCard as u, JsonLdObject as v, PersonInput as x, JsonLdValue as y };
@@ -0,0 +1,28 @@
1
+ import { C as jsonLd, S as WebSiteInput, _ as FaqItem, a as MetaTag, b as OrganizationInput, c as SeoInput, d as TwitterInput, f as defineSeo, g as BreadcrumbItem, h as ArticleLdInput, i as LinkTag, l as SiteConfig, m as seo, n as HeadTags, o as RobotsInput, p as robotsContent, r as ImageInput, s as Seo, t as ArticleInput, u as TwitterCard, v as JsonLdObject, x as PersonInput, y as JsonLdValue } from "./head-D2pPoGNP.js";
2
+ import { _ as absoluteUrl, a as ServerHandler, c as SitemapImage, d as UrlOptions, f as createSitemap, g as TrailingSlash, h as sitemapXml, i as RoutesFromTreeOptions, l as SitemapIndexEntry, m as sitemapIndexXml, n as CreateSitemapOptions, o as SitemapEntry, p as routesFromTree, r as HandlerContext, s as SitemapHandlers, t as ChangeFreq, u as SitemapSource } from "./sitemap-DbEixSCi.js";
3
+ type Awaitable<T> = T | Promise<T>;
4
+ interface RobotsRule {
5
+ userAgent: string | string[];
6
+ allow?: string | string[];
7
+ disallow?: string | string[];
8
+ crawlDelay?: number;
9
+ }
10
+ interface RobotsConfig {
11
+ /** Default: one rule allowing everything for every user agent. */
12
+ rules?: RobotsRule[];
13
+ /** Sitemap paths or URLs. Paths need `siteUrl`. */
14
+ sitemaps?: string[];
15
+ siteUrl?: string;
16
+ /** Block every crawler (`Disallow: /`), e.g. on staging or preview deployments. Overrides `rules`. */
17
+ disallowAll?: boolean;
18
+ }
19
+ /** Render a robots.txt body. */
20
+ export declare function robotsTxt(config?: RobotsConfig): string;
21
+ /**
22
+ * `GET` handler for a `/robots.txt` server route. Pass a function to decide per request
23
+ * (for example, `disallowAll` when the host is a preview deployment).
24
+ */
25
+ export declare function createRobotsHandler(config: RobotsConfig | ((ctx: HandlerContext) => Awaitable<RobotsConfig>), init?: {
26
+ cacheControl?: string;
27
+ }): ServerHandler;
28
+ export { type ArticleInput, type ArticleLdInput, type BreadcrumbItem, type ChangeFreq, type CreateSitemapOptions, type FaqItem, type HandlerContext, type HeadTags, type ImageInput, type JsonLdObject, type JsonLdValue, type LinkTag, type MetaTag, type OrganizationInput, type PersonInput, type RobotsConfig, type RobotsInput, type RobotsRule, type RoutesFromTreeOptions, type Seo, type SeoInput, type ServerHandler, type SiteConfig, type SitemapEntry, type SitemapHandlers, type SitemapImage, type SitemapIndexEntry, type SitemapSource, type TrailingSlash, type TwitterCard, type TwitterInput, type UrlOptions, type WebSiteInput, absoluteUrl, createSitemap, defineSeo, jsonLd, robotsContent, routesFromTree, seo, sitemapIndexXml, sitemapXml };