@businessdash/sdk 0.9.60 → 0.9.70

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.
Files changed (233) hide show
  1. package/README.md +1012 -186
  2. package/assets/biab-sw.js +92 -0
  3. package/dist/account-core/index.d.ts +126 -0
  4. package/dist/account-core/index.d.ts.map +1 -0
  5. package/dist/account-core/index.js +191 -0
  6. package/dist/account-core/index.js.map +1 -0
  7. package/dist/account.d.ts +11 -0
  8. package/dist/account.d.ts.map +1 -0
  9. package/dist/account.js +11 -0
  10. package/dist/account.js.map +1 -0
  11. package/dist/auth-handler.d.ts +1 -1
  12. package/dist/auth-links.d.ts +33 -0
  13. package/dist/auth-links.d.ts.map +1 -0
  14. package/dist/auth-links.js +64 -0
  15. package/dist/auth-links.js.map +1 -0
  16. package/dist/blog-core/index.d.ts +112 -0
  17. package/dist/blog-core/index.d.ts.map +1 -0
  18. package/dist/blog-core/index.js +330 -0
  19. package/dist/blog-core/index.js.map +1 -0
  20. package/dist/blog.d.ts +11 -0
  21. package/dist/blog.d.ts.map +1 -0
  22. package/dist/blog.js +11 -0
  23. package/dist/blog.js.map +1 -0
  24. package/dist/chat-core/index.d.ts +97 -0
  25. package/dist/chat-core/index.d.ts.map +1 -0
  26. package/dist/chat-core/index.js +237 -0
  27. package/dist/chat-core/index.js.map +1 -0
  28. package/dist/chat.d.ts +11 -0
  29. package/dist/chat.d.ts.map +1 -0
  30. package/dist/chat.js +11 -0
  31. package/dist/chat.js.map +1 -0
  32. package/dist/checkout-core/index.d.ts +65 -0
  33. package/dist/checkout-core/index.d.ts.map +1 -0
  34. package/dist/checkout-core/index.js +159 -0
  35. package/dist/checkout-core/index.js.map +1 -0
  36. package/dist/checkout.d.ts +10 -0
  37. package/dist/checkout.d.ts.map +1 -0
  38. package/dist/checkout.js +10 -0
  39. package/dist/checkout.js.map +1 -0
  40. package/dist/cli.js +133 -25
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client.d.ts +567 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +807 -1
  45. package/dist/client.js.map +1 -1
  46. package/dist/contracts.d.ts +3309 -761
  47. package/dist/contracts.d.ts.map +1 -1
  48. package/dist/contracts.js +541 -4
  49. package/dist/contracts.js.map +1 -1
  50. package/dist/data-model-schema.d.ts +1 -1
  51. package/dist/data-model-schema.d.ts.map +1 -1
  52. package/dist/element.d.ts +30 -0
  53. package/dist/element.d.ts.map +1 -1
  54. package/dist/element.js +56 -0
  55. package/dist/element.js.map +1 -1
  56. package/dist/embed-protocol.d.ts +86 -0
  57. package/dist/embed-protocol.d.ts.map +1 -0
  58. package/dist/embed-protocol.js +60 -0
  59. package/dist/embed-protocol.js.map +1 -0
  60. package/dist/framework-support.d.ts +122 -0
  61. package/dist/framework-support.d.ts.map +1 -0
  62. package/dist/framework-support.js +323 -0
  63. package/dist/framework-support.js.map +1 -0
  64. package/dist/index.cjs +1244 -5
  65. package/dist/legal-core/index.cjs +115 -0
  66. package/dist/legal-core/index.d.ts +160 -0
  67. package/dist/legal-core/index.d.ts.map +1 -0
  68. package/dist/legal-core/index.js +220 -0
  69. package/dist/legal-core/index.js.map +1 -0
  70. package/dist/marketing-core/index.d.ts +66 -0
  71. package/dist/marketing-core/index.d.ts.map +1 -0
  72. package/dist/marketing-core/index.js +154 -0
  73. package/dist/marketing-core/index.js.map +1 -0
  74. package/dist/marketing.d.ts +7 -0
  75. package/dist/marketing.d.ts.map +1 -0
  76. package/dist/marketing.js +7 -0
  77. package/dist/marketing.js.map +1 -0
  78. package/dist/mcp-adapters/index.d.ts +135 -0
  79. package/dist/mcp-adapters/index.d.ts.map +1 -0
  80. package/dist/mcp-adapters/index.js +187 -0
  81. package/dist/mcp-adapters/index.js.map +1 -0
  82. package/dist/mcp-adapters.d.ts +7 -0
  83. package/dist/mcp-adapters.d.ts.map +1 -0
  84. package/dist/mcp-adapters.js +7 -0
  85. package/dist/mcp-adapters.js.map +1 -0
  86. package/dist/model3d-core/index.cjs +95 -0
  87. package/dist/model3d-core/index.d.ts +167 -0
  88. package/dist/model3d-core/index.d.ts.map +1 -0
  89. package/dist/model3d-core/index.js +158 -0
  90. package/dist/model3d-core/index.js.map +1 -0
  91. package/dist/notifications-core/index.cjs +315 -0
  92. package/dist/notifications-core/index.d.ts +229 -0
  93. package/dist/notifications-core/index.d.ts.map +1 -0
  94. package/dist/notifications-core/index.js +445 -0
  95. package/dist/notifications-core/index.js.map +1 -0
  96. package/dist/portal-core/index.d.ts +451 -0
  97. package/dist/portal-core/index.d.ts.map +1 -0
  98. package/dist/portal-core/index.js +819 -0
  99. package/dist/portal-core/index.js.map +1 -0
  100. package/dist/portal.d.ts +7 -0
  101. package/dist/portal.d.ts.map +1 -0
  102. package/dist/portal.js +7 -0
  103. package/dist/portal.js.map +1 -0
  104. package/dist/push-core/index.cjs +171 -0
  105. package/dist/push-core/index.d.ts +149 -0
  106. package/dist/push-core/index.d.ts.map +1 -0
  107. package/dist/push-core/index.js +231 -0
  108. package/dist/push-core/index.js.map +1 -0
  109. package/dist/react-marketing.d.ts +1 -17
  110. package/dist/react-marketing.d.ts.map +1 -1
  111. package/dist/react-marketing.js +4 -0
  112. package/dist/react-marketing.js.map +1 -1
  113. package/dist/react.d.ts +294 -101
  114. package/dist/react.d.ts.map +1 -1
  115. package/dist/react.js +302 -246
  116. package/dist/react.js.map +1 -1
  117. package/dist/seed-core/index.d.ts +354 -0
  118. package/dist/seed-core/index.d.ts.map +1 -0
  119. package/dist/seed-core/index.js +420 -0
  120. package/dist/seed-core/index.js.map +1 -0
  121. package/dist/seed.d.ts +7 -0
  122. package/dist/seed.d.ts.map +1 -0
  123. package/dist/seed.js +7 -0
  124. package/dist/seed.js.map +1 -0
  125. package/dist/seo-core/blog.d.ts +94 -0
  126. package/dist/seo-core/blog.d.ts.map +1 -0
  127. package/dist/seo-core/blog.js +149 -0
  128. package/dist/seo-core/blog.js.map +1 -0
  129. package/dist/seo-core/commerce.d.ts +64 -0
  130. package/dist/seo-core/commerce.d.ts.map +1 -0
  131. package/dist/seo-core/commerce.js +87 -0
  132. package/dist/seo-core/commerce.js.map +1 -0
  133. package/dist/seo-core/index.cjs +751 -0
  134. package/dist/seo-core/index.d.ts +226 -0
  135. package/dist/seo-core/index.d.ts.map +1 -0
  136. package/dist/seo-core/index.js +455 -0
  137. package/dist/seo-core/index.js.map +1 -0
  138. package/dist/seo-core/local.d.ts +181 -0
  139. package/dist/seo-core/local.d.ts.map +1 -0
  140. package/dist/seo-core/local.js +193 -0
  141. package/dist/seo-core/local.js.map +1 -0
  142. package/dist/sitemap-core/index.cjs +167 -0
  143. package/dist/sitemap-core/index.d.ts +155 -0
  144. package/dist/sitemap-core/index.d.ts.map +1 -0
  145. package/dist/sitemap-core/index.js +195 -0
  146. package/dist/sitemap-core/index.js.map +1 -0
  147. package/dist/social-core/index.d.ts +74 -0
  148. package/dist/social-core/index.d.ts.map +1 -0
  149. package/dist/social-core/index.js +72 -0
  150. package/dist/social-core/index.js.map +1 -0
  151. package/dist/social.d.ts +7 -0
  152. package/dist/social.d.ts.map +1 -0
  153. package/dist/social.js +7 -0
  154. package/dist/social.js.map +1 -0
  155. package/dist/solid-compat.d.ts +204 -0
  156. package/dist/solid-compat.d.ts.map +1 -0
  157. package/dist/solid-compat.js +221 -0
  158. package/dist/solid-compat.js.map +1 -0
  159. package/dist/store-core/index.d.ts +141 -0
  160. package/dist/store-core/index.d.ts.map +1 -0
  161. package/dist/store-core/index.js +277 -0
  162. package/dist/store-core/index.js.map +1 -0
  163. package/dist/store.d.ts +12 -0
  164. package/dist/store.d.ts.map +1 -0
  165. package/dist/store.js +12 -0
  166. package/dist/store.js.map +1 -0
  167. package/dist/vanilla.d.ts +111 -0
  168. package/dist/vanilla.d.ts.map +1 -1
  169. package/dist/vanilla.js +206 -0
  170. package/dist/vanilla.js.map +1 -1
  171. package/dist/verify-core/index.d.ts +98 -0
  172. package/dist/verify-core/index.d.ts.map +1 -0
  173. package/dist/verify-core/index.js +186 -0
  174. package/dist/verify-core/index.js.map +1 -0
  175. package/dist/verify.d.ts +8 -0
  176. package/dist/verify.d.ts.map +1 -0
  177. package/dist/verify.js +8 -0
  178. package/dist/verify.js.map +1 -0
  179. package/openapi.json +4316 -1313
  180. package/package.json +121 -9
  181. package/src/account-core/index.ts +309 -0
  182. package/src/account.ts +11 -0
  183. package/src/angular/headless.ts +519 -0
  184. package/src/angular/index.ts +3 -0
  185. package/src/auth-links.ts +76 -0
  186. package/src/blog-core/index.ts +423 -0
  187. package/src/blog.ts +11 -0
  188. package/src/chat-core/index.ts +335 -0
  189. package/src/chat.ts +11 -0
  190. package/src/checkout-core/index.ts +225 -0
  191. package/src/checkout.ts +10 -0
  192. package/src/cli.ts +184 -28
  193. package/src/client.ts +998 -0
  194. package/src/contracts.ts +730 -11
  195. package/src/data-model-schema.ts +1 -1
  196. package/src/element.ts +102 -0
  197. package/src/embed-protocol.ts +100 -0
  198. package/src/framework-support.ts +440 -0
  199. package/src/legal-core/index.ts +276 -0
  200. package/src/marketing-core/index.ts +209 -0
  201. package/src/marketing.ts +7 -0
  202. package/src/mcp-adapters/index.ts +264 -0
  203. package/src/mcp-adapters.ts +7 -0
  204. package/src/model3d-core/index.ts +248 -0
  205. package/src/notifications-core/index.ts +650 -0
  206. package/src/portal-core/index.ts +1564 -0
  207. package/src/portal.ts +7 -0
  208. package/src/push-core/index.ts +335 -0
  209. package/src/qwik.tsx +602 -2
  210. package/src/react-marketing.tsx +5 -0
  211. package/src/react.tsx +434 -319
  212. package/src/seed-core/index.ts +735 -0
  213. package/src/seed.ts +7 -0
  214. package/src/seo-core/blog.ts +216 -0
  215. package/src/seo-core/commerce.ts +138 -0
  216. package/src/seo-core/index.ts +605 -0
  217. package/src/seo-core/local.ts +333 -0
  218. package/src/sitemap-core/index.ts +296 -0
  219. package/src/social-core/index.ts +103 -0
  220. package/src/social.ts +7 -0
  221. package/src/solid-compat.tsx +347 -0
  222. package/src/solid-surfaces.tsx +1311 -0
  223. package/src/solid.tsx +82 -60
  224. package/src/store-core/index.ts +426 -0
  225. package/src/store.ts +12 -0
  226. package/src/svelte/SocialLinks.svelte +84 -0
  227. package/src/svelte/headless.ts +435 -0
  228. package/src/svelte/index.ts +9 -0
  229. package/src/vanilla.ts +367 -0
  230. package/src/verify-core/index.ts +288 -0
  231. package/src/verify.ts +8 -0
  232. package/src/vue/headless.ts +527 -0
  233. package/src/vue/index.ts +3 -0
@@ -0,0 +1,751 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/seo-core/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ absoluteUrl: () => absoluteUrl,
24
+ applyToDocument: () => applyToDocument,
25
+ blogListingSeo: () => blogListingSeo,
26
+ blogPostSeo: () => blogPostSeo,
27
+ localServicePageSeo: () => localServicePageSeo,
28
+ mergeSeo: () => mergeSeo,
29
+ platformSeoToInput: () => platformSeoToInput,
30
+ productListingSeo: () => productListingSeo,
31
+ productPageSeo: () => productPageSeo,
32
+ renderHeadTags: () => renderHeadTags,
33
+ resolveSeo: () => resolveSeo,
34
+ servicesTimesAreas: () => servicesTimesAreas,
35
+ toHeadTags: () => toHeadTags,
36
+ toNextMetadata: () => toNextMetadata,
37
+ toNuxtHead: () => toNuxtHead,
38
+ toQwikDocumentHead: () => toQwikDocumentHead,
39
+ toRemixMeta: () => toRemixMeta,
40
+ toTanStackHead: () => toTanStackHead,
41
+ transactionalPageSeo: () => transactionalPageSeo
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+
45
+ // src/seo.ts
46
+ var SCHEMA_CTX = "https://schema.org";
47
+ function localBusiness(input) {
48
+ const node = {
49
+ "@context": SCHEMA_CTX,
50
+ "@type": input.businessType ?? "LocalBusiness",
51
+ "@id": input.id ?? `${input.siteUrl}#business`,
52
+ name: input.name,
53
+ url: input.url ?? input.siteUrl
54
+ };
55
+ if (input.parentOrganizationId) {
56
+ node["parentOrganization"] = { "@id": input.parentOrganizationId };
57
+ }
58
+ if (input.alternateName) node["alternateName"] = input.alternateName;
59
+ if (input.description) node["description"] = input.description;
60
+ if (input.telephone) node["telephone"] = input.telephone;
61
+ if (input.email) node["email"] = input.email;
62
+ if (input.priceRange) node["priceRange"] = input.priceRange;
63
+ if (input.address) {
64
+ node["address"] = { "@type": "PostalAddress", ...input.address };
65
+ }
66
+ if (input.geo) {
67
+ node["geo"] = { "@type": "GeoCoordinates", ...input.geo };
68
+ }
69
+ if (input.hasMap) node["hasMap"] = input.hasMap;
70
+ if (input.areaServed?.length) {
71
+ node["areaServed"] = input.areaServed.map(
72
+ (a) => typeof a === "string" ? { "@type": "City", name: a } : { "@type": a.type ?? "AdministrativeArea", name: a.name }
73
+ );
74
+ }
75
+ if (input.openingHours?.length) {
76
+ node["openingHoursSpecification"] = input.openingHours.map((h) => ({
77
+ "@type": "OpeningHoursSpecification",
78
+ dayOfWeek: h.dayOfWeek,
79
+ opens: h.opens,
80
+ closes: h.closes
81
+ }));
82
+ }
83
+ if (input.knowsAbout?.length) node["knowsAbout"] = [...input.knowsAbout];
84
+ if (input.aggregateRating) {
85
+ node["aggregateRating"] = {
86
+ "@type": "AggregateRating",
87
+ ratingValue: String(input.aggregateRating.ratingValue),
88
+ reviewCount: String(input.aggregateRating.reviewCount),
89
+ bestRating: String(input.aggregateRating.bestRating ?? "5")
90
+ };
91
+ }
92
+ if (input.contactPoint?.length) {
93
+ node["contactPoint"] = input.contactPoint.map((c) => ({
94
+ "@type": "ContactPoint",
95
+ ...c
96
+ }));
97
+ }
98
+ return node;
99
+ }
100
+ function service(input) {
101
+ const node = {
102
+ "@context": SCHEMA_CTX,
103
+ "@type": "Service",
104
+ serviceType: input.title,
105
+ name: input.title,
106
+ description: input.description,
107
+ url: `${input.siteUrl}/services#${input.slug}`,
108
+ provider: { "@id": input.providerId ?? `${input.siteUrl}#business` }
109
+ };
110
+ if (input.areaServed?.length) node["areaServed"] = [...input.areaServed];
111
+ if (input.audience) {
112
+ node["audience"] = { "@type": "Audience", audienceType: input.audience };
113
+ }
114
+ return node;
115
+ }
116
+ function product(input) {
117
+ const node = {
118
+ "@context": SCHEMA_CTX,
119
+ "@type": "Product",
120
+ name: input.name,
121
+ url: input.url
122
+ };
123
+ if (input.description) node["description"] = input.description;
124
+ if (input.image) {
125
+ node["image"] = Array.isArray(input.image) ? [...input.image] : input.image;
126
+ }
127
+ if (input.brand) node["brand"] = { "@type": "Brand", name: input.brand };
128
+ if (input.sku) node["sku"] = input.sku;
129
+ if (input.offer) {
130
+ const offer = {
131
+ "@type": "Offer",
132
+ price: (input.offer.priceCents / 100).toFixed(2),
133
+ priceCurrency: input.offer.currency ?? "USD",
134
+ availability: `${SCHEMA_CTX}/${input.offer.availability ?? "InStock"}`,
135
+ url: input.url
136
+ };
137
+ if (input.sellerId) offer["seller"] = { "@id": input.sellerId };
138
+ node["offers"] = offer;
139
+ }
140
+ return node;
141
+ }
142
+ function breadcrumb(input) {
143
+ return {
144
+ "@context": SCHEMA_CTX,
145
+ "@type": "BreadcrumbList",
146
+ itemListElement: input.items.map((item, i) => ({
147
+ "@type": "ListItem",
148
+ position: i + 1,
149
+ name: item.name,
150
+ item: item.url.startsWith("http") ? item.url : `${input.siteUrl}${item.url}`
151
+ }))
152
+ };
153
+ }
154
+ function toIsoDate(d) {
155
+ if (typeof d === "string") return d;
156
+ return d.toISOString();
157
+ }
158
+ function article(input) {
159
+ const node = {
160
+ "@context": SCHEMA_CTX,
161
+ "@type": "BlogPosting",
162
+ headline: input.headline,
163
+ description: input.description,
164
+ datePublished: toIsoDate(input.pubDate),
165
+ dateModified: toIsoDate(input.updatedDate ?? input.pubDate),
166
+ mainEntityOfPage: `${input.siteUrl}/blog/${input.slug}`,
167
+ author: input.authorOrgId ? { "@id": input.authorOrgId } : { "@type": "Person", name: input.authorName ?? "Editorial team" },
168
+ publisher: { "@id": input.publisherId ?? `${input.siteUrl}#business` }
169
+ };
170
+ if (input.heroImageUrl) node["image"] = input.heroImageUrl;
171
+ return node;
172
+ }
173
+
174
+ // src/seo-core/blog.ts
175
+ function isGated(accessLevel) {
176
+ const value = (accessLevel ?? "public").trim();
177
+ return value !== "public" && value !== "";
178
+ }
179
+ function blogPostSeo(input) {
180
+ const { post } = input;
181
+ const path = input.path ?? `/blog/${post.slug}`;
182
+ const canonical = `${input.siteUrl.replace(/\/+$/, "")}${path}`;
183
+ const gated = isGated(post.accessLevel);
184
+ const description = post.excerpt?.trim() || void 0;
185
+ const node = article({
186
+ siteUrl: input.siteUrl,
187
+ headline: post.title,
188
+ description: description ?? post.title,
189
+ slug: post.slug,
190
+ pubDate: post.publishedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
191
+ ...post.updatedAt ? { updatedDate: post.updatedAt } : {},
192
+ ...post.authorName ? { authorName: post.authorName } : {},
193
+ ...post.imageUrl ? { heroImageUrl: post.imageUrl } : {}
194
+ });
195
+ node["mainEntityOfPage"] = canonical;
196
+ node["url"] = canonical;
197
+ if (post.tags?.length) node["keywords"] = post.tags.join(", ");
198
+ node["isAccessibleForFree"] = !gated;
199
+ if (gated) {
200
+ node["hasPart"] = {
201
+ "@type": "WebPageElement",
202
+ isAccessibleForFree: false,
203
+ cssSelector: input.paywallSelector ?? ".biab-post-body"
204
+ };
205
+ }
206
+ const extraMeta = [];
207
+ if (post.publishedAt) {
208
+ extraMeta.push({
209
+ property: "article:published_time",
210
+ content: post.publishedAt
211
+ });
212
+ }
213
+ if (post.updatedAt) {
214
+ extraMeta.push({
215
+ property: "article:modified_time",
216
+ content: post.updatedAt
217
+ });
218
+ }
219
+ if (post.authorName) {
220
+ extraMeta.push({ property: "article:author", content: post.authorName });
221
+ }
222
+ for (const tag of post.tags ?? []) {
223
+ extraMeta.push({ property: "article:tag", content: tag });
224
+ }
225
+ return {
226
+ title: post.title,
227
+ ...description ? { description } : {},
228
+ canonical,
229
+ ...post.imageUrl ? { ogImage: post.imageUrl } : {},
230
+ // Not "website": social cards for articles show the date and byline.
231
+ ogType: "article",
232
+ ...post.tags?.length ? { keywords: post.tags } : {},
233
+ extraMeta,
234
+ jsonLd: [
235
+ node,
236
+ breadcrumb({
237
+ siteUrl: input.siteUrl,
238
+ items: [
239
+ { name: "Home", url: input.siteUrl },
240
+ { name: "Blog", url: `${input.siteUrl.replace(/\/+$/, "")}/blog` },
241
+ ...input.breadcrumbs ?? [],
242
+ { name: post.title, url: canonical }
243
+ ]
244
+ })
245
+ ]
246
+ };
247
+ }
248
+ function blogListingSeo(input) {
249
+ const base = input.siteUrl.replace(/\/+$/, "");
250
+ const path = input.path ?? "/blog";
251
+ const canonical = `${base}${path}`;
252
+ const postPath = input.postPath ?? ((p) => `/blog/${p.slug}`);
253
+ const page = input.page ?? 1;
254
+ const node = {
255
+ "@context": "https://schema.org",
256
+ "@type": "Blog",
257
+ url: canonical,
258
+ ...input.title ? { name: input.title } : {},
259
+ ...input.description ? { description: input.description } : {},
260
+ blogPost: input.posts.map((post) => ({
261
+ "@type": "BlogPosting",
262
+ headline: post.title,
263
+ url: `${base}${postPath(post)}`,
264
+ ...post.publishedAt ? { datePublished: post.publishedAt } : {},
265
+ ...post.imageUrl ? { image: post.imageUrl } : {}
266
+ }))
267
+ };
268
+ return {
269
+ ...input.title ? { title: input.title } : {},
270
+ ...input.description ? { description: input.description } : {},
271
+ canonical,
272
+ // Thin archive pages competing with the posts themselves splits the
273
+ // signal; the posts are what should rank.
274
+ ...page > 1 ? { noIndex: true } : {},
275
+ jsonLd: [node]
276
+ };
277
+ }
278
+
279
+ // src/seo-core/commerce.ts
280
+ function productListingSeo(input) {
281
+ const base = input.siteUrl.replace(/\/+$/, "");
282
+ const page = input.page ?? 1;
283
+ const canonicalPath = input.filtered && input.unfilteredPath ? input.unfilteredPath : input.path;
284
+ const canonical = `${base}${canonicalPath}`;
285
+ const absolute = (url) => /^[a-z][a-z0-9+.-]*:|^\/\//i.test(url) ? url : `${base}${url.startsWith("/") ? "" : "/"}${url}`;
286
+ const node = {
287
+ "@context": "https://schema.org",
288
+ "@type": "CollectionPage",
289
+ url: canonical,
290
+ ...input.title ? { name: input.title } : {},
291
+ ...input.description ? { description: input.description } : {},
292
+ mainEntity: {
293
+ "@type": "ItemList",
294
+ numberOfItems: input.products.length,
295
+ itemListElement: input.products.map((product2, index) => ({
296
+ "@type": "ListItem",
297
+ position: index + 1,
298
+ item: {
299
+ "@type": "Product",
300
+ name: product2.name,
301
+ url: absolute(product2.url),
302
+ ...product2.image ? { image: absolute(product2.image) } : {},
303
+ ...typeof product2.priceCents === "number" ? {
304
+ offers: {
305
+ "@type": "Offer",
306
+ price: (product2.priceCents / 100).toFixed(2),
307
+ priceCurrency: product2.currency ?? "USD",
308
+ ...product2.availability ? {
309
+ availability: `https://schema.org/${product2.availability}`
310
+ } : {}
311
+ }
312
+ } : {}
313
+ }
314
+ }))
315
+ }
316
+ };
317
+ return {
318
+ ...input.title ? { title: input.title } : {},
319
+ ...input.description ? { description: input.description } : {},
320
+ canonical,
321
+ // Either reason is enough on its own to keep the page out of the index.
322
+ ...page > 1 || input.filtered ? { noIndex: page > 1 } : {},
323
+ jsonLd: [
324
+ node,
325
+ breadcrumb({
326
+ siteUrl: input.siteUrl,
327
+ items: [
328
+ { name: "Home", url: input.siteUrl },
329
+ ...input.breadcrumbs ?? [],
330
+ ...input.title ? [{ name: input.title, url: canonical }] : []
331
+ ]
332
+ })
333
+ ]
334
+ };
335
+ }
336
+ function transactionalPageSeo(title) {
337
+ return { title, noIndex: true, canonical: null };
338
+ }
339
+
340
+ // src/seo-core/local.ts
341
+ function areaLabel(area) {
342
+ return area.region ? `${area.name}, ${area.region}` : area.name;
343
+ }
344
+ function localServicePageSeo(input) {
345
+ const label = areaLabel(input.area);
346
+ const written = input.describe?.({
347
+ service: input.service,
348
+ area: input.area
349
+ });
350
+ const title = written?.title ?? `${input.service.name} in ${label}`;
351
+ const description = written?.description ?? `${input.service.description} Serving ${label} and the surrounding area.`;
352
+ const canonical = `${input.siteUrl.replace(/\/+$/, "")}${input.path}`;
353
+ const jsonLd = [
354
+ service({
355
+ siteUrl: input.siteUrl,
356
+ slug: input.service.slug,
357
+ title: input.service.name,
358
+ description,
359
+ // The whole point of the page: this service, in THIS place. A node
360
+ // that omits it is indistinguishable from the generic service page.
361
+ areaServed: [label]
362
+ })
363
+ ];
364
+ if (input.business) jsonLd.push(localBusiness(input.business));
365
+ jsonLd.push(
366
+ breadcrumb({
367
+ siteUrl: input.siteUrl,
368
+ items: [
369
+ { name: "Home", url: input.siteUrl },
370
+ ...input.breadcrumbs ?? [],
371
+ { name: `${input.service.name} in ${label}`, url: canonical }
372
+ ]
373
+ })
374
+ );
375
+ return {
376
+ title,
377
+ description,
378
+ canonical,
379
+ ...input.ogImage ? { ogImage: input.ogImage } : {},
380
+ ...input.noIndex === void 0 ? {} : { noIndex: input.noIndex },
381
+ jsonLd
382
+ };
383
+ }
384
+ function productPageSeo(input) {
385
+ const canonical = `${input.siteUrl.replace(/\/+$/, "")}${input.path}`;
386
+ const p = input.product;
387
+ const jsonLd = [
388
+ product({
389
+ url: canonical,
390
+ name: p.name,
391
+ ...p.description ? { description: p.description } : {},
392
+ ...p.image ? { image: p.image } : {},
393
+ ...p.brand ? { brand: p.brand } : {},
394
+ ...p.sku ? { sku: p.sku } : {},
395
+ ...p.priceCents === void 0 ? {} : {
396
+ offer: {
397
+ priceCents: p.priceCents,
398
+ ...p.currency ? { currency: p.currency } : {},
399
+ ...p.availability ? { availability: p.availability } : {}
400
+ }
401
+ }
402
+ }),
403
+ breadcrumb({
404
+ siteUrl: input.siteUrl,
405
+ items: [
406
+ { name: "Home", url: input.siteUrl },
407
+ ...input.breadcrumbs ?? [],
408
+ { name: p.name, url: canonical }
409
+ ]
410
+ })
411
+ ];
412
+ const productJson = jsonLd[0];
413
+ if (typeof p.avgRating === "number" && typeof p.reviewCount === "number" && p.reviewCount > 0) {
414
+ productJson["aggregateRating"] = {
415
+ "@type": "AggregateRating",
416
+ ratingValue: p.avgRating,
417
+ reviewCount: p.reviewCount
418
+ };
419
+ }
420
+ if (p.priceRangeCents && p.priceRangeCents.high > p.priceRangeCents.low) {
421
+ productJson["offers"] = {
422
+ "@type": "AggregateOffer",
423
+ priceCurrency: p.currency ?? "USD",
424
+ lowPrice: (p.priceRangeCents.low / 100).toFixed(2),
425
+ highPrice: (p.priceRangeCents.high / 100).toFixed(2),
426
+ ...p.availability ? { availability: `https://schema.org/${p.availability}` } : {}
427
+ };
428
+ }
429
+ return {
430
+ title: p.name,
431
+ ...p.description ? { description: p.description } : {},
432
+ canonical,
433
+ ...p.image ? { ogImage: Array.isArray(p.image) ? p.image[0] : p.image } : {},
434
+ // Products are things people buy, so the card should show the thing.
435
+ ogType: "product",
436
+ ...input.noIndex === void 0 ? {} : { noIndex: input.noIndex },
437
+ jsonLd
438
+ };
439
+ }
440
+ function servicesTimesAreas(input) {
441
+ const plans = [];
442
+ for (const service2 of input.services) {
443
+ for (const area of input.areas) {
444
+ if (input.include && !input.include(service2, area)) continue;
445
+ const path = input.path(service2, area);
446
+ plans.push({
447
+ path,
448
+ service: service2,
449
+ area,
450
+ seo: localServicePageSeo({
451
+ siteUrl: input.siteUrl,
452
+ service: service2,
453
+ area,
454
+ path,
455
+ ...input.describe ? { describe: input.describe } : {},
456
+ ...input.business ? { business: input.business } : {}
457
+ })
458
+ });
459
+ }
460
+ }
461
+ return plans;
462
+ }
463
+
464
+ // src/seo-core/index.ts
465
+ function isBlank(value) {
466
+ return value === null || value === void 0 || value.trim() === "";
467
+ }
468
+ function absoluteUrl(url, baseUrl) {
469
+ if (isBlank(url)) return null;
470
+ const value = url.trim();
471
+ if (/^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith("//")) {
472
+ return value;
473
+ }
474
+ if (isBlank(baseUrl)) {
475
+ return null;
476
+ }
477
+ const base = baseUrl.replace(/\/+$/, "");
478
+ return `${base}${value.startsWith("/") ? "" : "/"}${value}`;
479
+ }
480
+ function platformSeoToInput(seo) {
481
+ if (!seo) return {};
482
+ return {
483
+ title: seo.seoTitle,
484
+ description: seo.seoDescription,
485
+ canonical: seo.canonicalUrl,
486
+ noIndex: seo.noIndex,
487
+ ogTitle: seo.ogTitle,
488
+ ogDescription: seo.ogDescription,
489
+ ogImage: seo.ogImageUrl,
490
+ twitterCard: seo.twitterCard,
491
+ keywords: seo.keywords,
492
+ jsonLd: seo.jsonldNodes,
493
+ hreflang: seo.hreflang
494
+ };
495
+ }
496
+ function mergeSeo(...layers) {
497
+ const out = {};
498
+ const jsonLd = [];
499
+ const extraMeta = [];
500
+ for (const layer of layers) {
501
+ if (!layer) continue;
502
+ for (const [key, value] of Object.entries(layer)) {
503
+ if (value === void 0) continue;
504
+ if (key === "jsonLd") {
505
+ if (Array.isArray(value)) jsonLd.push(...value);
506
+ continue;
507
+ }
508
+ if (key === "extraMeta") {
509
+ if (Array.isArray(value)) extraMeta.push(...value);
510
+ continue;
511
+ }
512
+ if (key === "hreflang") {
513
+ out.hreflang = {
514
+ ...out.hreflang ?? {},
515
+ ...value
516
+ };
517
+ continue;
518
+ }
519
+ out[key] = value;
520
+ }
521
+ }
522
+ if (jsonLd.length > 0) out.jsonLd = jsonLd;
523
+ if (extraMeta.length > 0) out.extraMeta = extraMeta;
524
+ return out;
525
+ }
526
+ function resolveSeo(...layers) {
527
+ const seo = mergeSeo(...layers);
528
+ const baseTitle = isBlank(seo.title) ? null : seo.title.trim();
529
+ const title = baseTitle && seo.titleSuffix ? `${baseTitle}${seo.titleSuffix}` : baseTitle;
530
+ const description = isBlank(seo.description) ? null : seo.description.trim();
531
+ const canonical = absoluteUrl(seo.canonical, seo.baseUrl);
532
+ const ogImage = absoluteUrl(seo.ogImage, seo.baseUrl);
533
+ const noIndex = seo.noIndex === true;
534
+ const meta = [];
535
+ const push = (tag) => {
536
+ if (!isBlank(tag.content)) meta.push(tag);
537
+ };
538
+ if (description) push({ name: "description", content: description });
539
+ if (seo.keywords?.length) {
540
+ push({ name: "keywords", content: seo.keywords.join(", ") });
541
+ }
542
+ const robots = noIndex ? "noindex, nofollow" : "index, follow";
543
+ push({ name: "robots", content: robots });
544
+ const ogTitle = isBlank(seo.ogTitle) ? title : seo.ogTitle;
545
+ const ogDescription = isBlank(seo.ogDescription) ? description : seo.ogDescription;
546
+ if (ogTitle) push({ property: "og:title", content: ogTitle });
547
+ if (ogDescription)
548
+ push({ property: "og:description", content: ogDescription });
549
+ if (ogImage) push({ property: "og:image", content: ogImage });
550
+ if (canonical) push({ property: "og:url", content: canonical });
551
+ push({ property: "og:type", content: seo.ogType ?? "website" });
552
+ if (seo.siteName) push({ property: "og:site_name", content: seo.siteName });
553
+ if (seo.locale) push({ property: "og:locale", content: seo.locale });
554
+ push({
555
+ name: "twitter:card",
556
+ content: seo.twitterCard ?? "summary_large_image"
557
+ });
558
+ if (seo.twitterSite) push({ name: "twitter:site", content: seo.twitterSite });
559
+ if (ogTitle) push({ name: "twitter:title", content: ogTitle });
560
+ if (ogDescription)
561
+ push({ name: "twitter:description", content: ogDescription });
562
+ if (ogImage) push({ name: "twitter:image", content: ogImage });
563
+ for (const tag of seo.extraMeta ?? []) push(tag);
564
+ const links = [];
565
+ if (canonical) links.push({ rel: "canonical", href: canonical });
566
+ for (const [lang, href] of Object.entries(seo.hreflang ?? {})) {
567
+ const resolved = absoluteUrl(href, seo.baseUrl);
568
+ if (resolved)
569
+ links.push({ rel: "alternate", href: resolved, hreflang: lang });
570
+ }
571
+ return {
572
+ title,
573
+ description,
574
+ canonical,
575
+ robots,
576
+ noIndex,
577
+ meta,
578
+ links,
579
+ jsonLd: seo.jsonLd ?? []
580
+ };
581
+ }
582
+ function toNextMetadata(seo) {
583
+ const openGraph = {};
584
+ const twitter = {};
585
+ const other = {};
586
+ for (const tag of seo.meta) {
587
+ if (tag.property?.startsWith("og:")) {
588
+ openGraph[tag.property.slice(3)] = tag.content;
589
+ } else if (tag.name?.startsWith("twitter:")) {
590
+ twitter[tag.name.slice(8)] = tag.content;
591
+ } else if (tag.name && tag.name !== "description" && tag.name !== "robots") {
592
+ other[tag.name] = tag.content;
593
+ }
594
+ }
595
+ const metadata = {
596
+ ...seo.title ? { title: seo.title } : {},
597
+ ...seo.description ? { description: seo.description } : {},
598
+ // Next wants a structured robots object, not the string.
599
+ robots: { index: !seo.noIndex, follow: !seo.noIndex },
600
+ ...seo.canonical ? { alternates: { canonical: seo.canonical } } : {},
601
+ ...Object.keys(openGraph).length ? { openGraph } : {},
602
+ ...Object.keys(twitter).length ? { twitter } : {},
603
+ ...Object.keys(other).length ? { other } : {}
604
+ };
605
+ const alternates = metadata.alternates;
606
+ const languages = {};
607
+ for (const link of seo.links) {
608
+ if (link.rel === "alternate" && link.hreflang) {
609
+ languages[link.hreflang] = link.href;
610
+ }
611
+ }
612
+ if (Object.keys(languages).length > 0) {
613
+ metadata.alternates = { ...alternates ?? {}, languages };
614
+ }
615
+ return metadata;
616
+ }
617
+ function toRemixMeta(seo) {
618
+ const out = [];
619
+ if (seo.title) out.push({ title: seo.title });
620
+ for (const tag of seo.meta) {
621
+ out.push(
622
+ tag.property ? { property: tag.property, content: tag.content } : { name: tag.name, content: tag.content }
623
+ );
624
+ }
625
+ for (const link of seo.links) {
626
+ out.push({ tagName: "link", ...link });
627
+ }
628
+ for (const node of seo.jsonLd) {
629
+ out.push({ "script:ld+json": node });
630
+ }
631
+ return out;
632
+ }
633
+ function toNuxtHead(seo) {
634
+ return {
635
+ ...seo.title ? { title: seo.title } : {},
636
+ meta: seo.meta.map(
637
+ (tag) => tag.property ? { property: tag.property, content: tag.content } : { name: tag.name, content: tag.content }
638
+ ),
639
+ link: seo.links.map((link) => ({ ...link })),
640
+ script: seo.jsonLd.map((node) => ({
641
+ type: "application/ld+json",
642
+ innerHTML: JSON.stringify(node)
643
+ }))
644
+ };
645
+ }
646
+ function toQwikDocumentHead(seo) {
647
+ return {
648
+ ...seo.title ? { title: seo.title } : {},
649
+ meta: seo.meta.map(
650
+ (tag) => tag.property ? { property: tag.property, content: tag.content } : { name: tag.name ?? "", content: tag.content }
651
+ ),
652
+ links: seo.links.map((link) => ({ ...link })),
653
+ // Qwik has no JSON-LD slot on DocumentHead; carry it in `frontmatter`
654
+ // so the route can render it into the body.
655
+ frontmatter: { jsonLd: seo.jsonLd }
656
+ };
657
+ }
658
+ function toTanStackHead(seo) {
659
+ const meta = [];
660
+ if (seo.title) meta.push({ title: seo.title });
661
+ for (const tag of seo.meta) {
662
+ meta.push(
663
+ tag.property ? { property: tag.property, content: tag.content } : { name: tag.name, content: tag.content }
664
+ );
665
+ }
666
+ return {
667
+ meta,
668
+ links: seo.links.map((link) => ({ ...link })),
669
+ scripts: seo.jsonLd.map((node) => ({
670
+ type: "application/ld+json",
671
+ children: JSON.stringify(node)
672
+ }))
673
+ };
674
+ }
675
+ function toHeadTags(seo) {
676
+ const tags = [];
677
+ if (seo.title) tags.push({ tag: "title", attrs: {}, children: seo.title });
678
+ for (const tag of seo.meta) {
679
+ tags.push({
680
+ tag: "meta",
681
+ attrs: tag.property ? { property: tag.property, content: tag.content } : { name: tag.name ?? "", content: tag.content }
682
+ });
683
+ }
684
+ for (const link of seo.links) {
685
+ const attrs = { rel: link.rel, href: link.href };
686
+ if (link.hreflang) attrs.hreflang = link.hreflang;
687
+ tags.push({ tag: "link", attrs });
688
+ }
689
+ for (const node of seo.jsonLd) {
690
+ tags.push({
691
+ tag: "script",
692
+ attrs: { type: "application/ld+json" },
693
+ children: JSON.stringify(node)
694
+ });
695
+ }
696
+ return tags;
697
+ }
698
+ function renderHeadTags(seo) {
699
+ const escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
700
+ return toHeadTags(seo).map((tag) => {
701
+ const attrs = Object.entries(tag.attrs).map(([key, value]) => `${key}="${escapeAttr(value)}"`).join(" ");
702
+ if (tag.tag === "title") {
703
+ return `<title>${escapeAttr(tag.children ?? "")}</title>`;
704
+ }
705
+ if (tag.tag === "script") {
706
+ const payload = (tag.children ?? "").replace(/</g, "\\u003c");
707
+ return `<script ${attrs}>${payload}</script>`;
708
+ }
709
+ return `<${tag.tag} ${attrs}>`;
710
+ }).join("\n");
711
+ }
712
+ function applyToDocument(seo, doc = typeof document === "undefined" ? void 0 : document) {
713
+ if (!doc) return;
714
+ const OWNED = "data-biab-seo";
715
+ for (const existing of Array.from(doc.head.querySelectorAll(`[${OWNED}]`))) {
716
+ existing.remove();
717
+ }
718
+ if (seo.title !== null) doc.title = seo.title;
719
+ for (const tag of toHeadTags(seo)) {
720
+ if (tag.tag === "title") continue;
721
+ const element = doc.createElement(tag.tag);
722
+ for (const [key, value] of Object.entries(tag.attrs)) {
723
+ element.setAttribute(key, value);
724
+ }
725
+ if (tag.children) element.textContent = tag.children;
726
+ element.setAttribute(OWNED, "");
727
+ doc.head.appendChild(element);
728
+ }
729
+ }
730
+ // Annotate the CommonJS export names for ESM import in node:
731
+ 0 && (module.exports = {
732
+ absoluteUrl,
733
+ applyToDocument,
734
+ blogListingSeo,
735
+ blogPostSeo,
736
+ localServicePageSeo,
737
+ mergeSeo,
738
+ platformSeoToInput,
739
+ productListingSeo,
740
+ productPageSeo,
741
+ renderHeadTags,
742
+ resolveSeo,
743
+ servicesTimesAreas,
744
+ toHeadTags,
745
+ toNextMetadata,
746
+ toNuxtHead,
747
+ toQwikDocumentHead,
748
+ toRemixMeta,
749
+ toTanStackHead,
750
+ transactionalPageSeo
751
+ });