@stack-mode/site-manager-kit 1.44.0 → 1.45.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.
Files changed (61) hide show
  1. package/README.md +618 -606
  2. package/audit/ts-hooks.mjs +68 -68
  3. package/audit/wiring-audit.d.mts +37 -37
  4. package/audit/wiring-audit.mjs +604 -604
  5. package/bin/site-manager-kit.mjs +10 -10
  6. package/package.json +73 -72
  7. package/src/announcements.ts +13 -13
  8. package/src/attribution-fields.ts +52 -52
  9. package/src/attribution.tsx +47 -47
  10. package/src/blog.ts +122 -122
  11. package/src/chat-route.ts +136 -136
  12. package/src/chat-types.ts +76 -76
  13. package/src/client.ts +116 -116
  14. package/src/config.ts +78 -78
  15. package/src/copy-keys-route.ts +21 -21
  16. package/src/copy-keys.ts +77 -77
  17. package/src/data.ts +189 -189
  18. package/src/downloads.ts +7 -7
  19. package/src/faq.ts +21 -21
  20. package/src/floor-plans.ts +45 -45
  21. package/src/form-guard.tsx +37 -37
  22. package/src/form-types.ts +13 -13
  23. package/src/forms.ts +76 -76
  24. package/src/glossary.ts +19 -19
  25. package/src/health-route.ts +34 -34
  26. package/src/index.ts +36 -36
  27. package/src/indexnow.ts +40 -40
  28. package/src/listings.ts +38 -38
  29. package/src/llms-route.ts +45 -0
  30. package/src/metadata.ts +55 -55
  31. package/src/not-found-reporter.tsx +16 -16
  32. package/src/not-found.ts +53 -53
  33. package/src/payment.ts +70 -70
  34. package/src/phone-input.tsx +26 -26
  35. package/src/phone.ts +37 -37
  36. package/src/preview-route.ts +48 -48
  37. package/src/preview.ts +94 -94
  38. package/src/residents.ts +23 -23
  39. package/src/revalidate-route.ts +34 -34
  40. package/src/robots.ts +5 -1
  41. package/src/rss-route.ts +26 -26
  42. package/src/services.ts +52 -52
  43. package/src/site-chat-panel.tsx +537 -537
  44. package/src/site-chat.tsx +26 -26
  45. package/src/sitemap.ts +26 -26
  46. package/src/structured-data.tsx +207 -207
  47. package/src/types.ts +581 -580
  48. package/src/version.ts +1 -1
  49. package/src/visual-accents.ts +126 -126
  50. package/src/visual-bridge-client.tsx +1070 -1070
  51. package/src/visual-bridge.tsx +30 -30
  52. package/src/visual-clicks.ts +13 -13
  53. package/src/visual-lists.ts +37 -37
  54. package/src/visual-match.ts +77 -77
  55. package/src/visual-messages.ts +160 -160
  56. package/src/visual-mode.ts +18 -18
  57. package/src/visual-preview.ts +69 -69
  58. package/src/visual-tags.ts +90 -90
  59. package/src/visual.ts +59 -59
  60. package/src/waitlist-form.ts +39 -39
  61. package/src/waitlist.ts +48 -48
package/src/services.ts CHANGED
@@ -1,52 +1,52 @@
1
- import type { Service, Site } from "./types";
2
-
3
- type Json = Record<string, unknown>;
4
-
5
- /**
6
- * schema.org `Service` structured data for a service page, with the business as its provider. A price becomes an
7
- * `Offer` (a range as a `PriceSpecification` with min and max); "call for pricing" and no price add no offer, so
8
- * search engines are never told a price the business didn't give. Amounts stay decimal strings. Prices are in USD,
9
- * the only currency Site Manager's sites use.
10
- */
11
- export function serviceJsonLd(service: Service, site: Pick<Site, "name" | "business" | "websiteUrl">, url: string): Json {
12
- const b = site.business;
13
- const a = b.address;
14
- const provider: Json = {
15
- "@type": "LocalBusiness",
16
- name: b.displayName || site.name,
17
- ...(site.websiteUrl ? { url: site.websiteUrl } : {}),
18
- ...(b.phone ? { telephone: b.phone } : {}),
19
- ...(a.line1 || a.city
20
- ? {
21
- address: {
22
- "@type": "PostalAddress",
23
- ...(a.line1 ? { streetAddress: [a.line1, a.line2].filter(Boolean).join(", ") } : {}),
24
- ...(a.city ? { addressLocality: a.city } : {}),
25
- ...(a.region ? { addressRegion: a.region } : {}),
26
- ...(a.postalCode ? { postalCode: a.postalCode } : {}),
27
- ...(a.country ? { addressCountry: a.country } : {}),
28
- },
29
- }
30
- : {}),
31
- };
32
- const p = service.price;
33
- let offers: Json | null = null;
34
- if (p.kind === "fixed" && p.from) offers = { "@type": "Offer", priceCurrency: "USD", price: p.from };
35
- else if (p.kind === "starting_at" && p.from) {
36
- offers = { "@type": "Offer", priceCurrency: "USD", price: p.from, priceSpecification: { "@type": "PriceSpecification", priceCurrency: "USD", minPrice: p.from } };
37
- } else if (p.kind === "range" && p.from && p.to) {
38
- offers = { "@type": "Offer", priceCurrency: "USD", priceSpecification: { "@type": "PriceSpecification", priceCurrency: "USD", minPrice: p.from, maxPrice: p.to } };
39
- }
40
- return {
41
- "@context": "https://schema.org",
42
- "@type": "Service",
43
- name: service.name,
44
- url,
45
- ...(service.summary ? { description: service.summary } : {}),
46
- ...(service.category ? { serviceType: service.category } : {}),
47
- ...(service.photos.length ? { image: service.photos.map((ph) => ph.url) } : {}),
48
- provider,
49
- ...(a.city ? { areaServed: a.region ? `${a.city}, ${a.region}` : a.city } : {}),
50
- ...(offers ? { offers } : {}),
51
- };
52
- }
1
+ import type { Service, Site } from "./types";
2
+
3
+ type Json = Record<string, unknown>;
4
+
5
+ /**
6
+ * schema.org `Service` structured data for a service page, with the business as its provider. A price becomes an
7
+ * `Offer` (a range as a `PriceSpecification` with min and max); "call for pricing" and no price add no offer, so
8
+ * search engines are never told a price the business didn't give. Amounts stay decimal strings. Prices are in USD,
9
+ * the only currency Site Manager's sites use.
10
+ */
11
+ export function serviceJsonLd(service: Service, site: Pick<Site, "name" | "business" | "websiteUrl">, url: string): Json {
12
+ const b = site.business;
13
+ const a = b.address;
14
+ const provider: Json = {
15
+ "@type": "LocalBusiness",
16
+ name: b.displayName || site.name,
17
+ ...(site.websiteUrl ? { url: site.websiteUrl } : {}),
18
+ ...(b.phone ? { telephone: b.phone } : {}),
19
+ ...(a.line1 || a.city
20
+ ? {
21
+ address: {
22
+ "@type": "PostalAddress",
23
+ ...(a.line1 ? { streetAddress: [a.line1, a.line2].filter(Boolean).join(", ") } : {}),
24
+ ...(a.city ? { addressLocality: a.city } : {}),
25
+ ...(a.region ? { addressRegion: a.region } : {}),
26
+ ...(a.postalCode ? { postalCode: a.postalCode } : {}),
27
+ ...(a.country ? { addressCountry: a.country } : {}),
28
+ },
29
+ }
30
+ : {}),
31
+ };
32
+ const p = service.price;
33
+ let offers: Json | null = null;
34
+ if (p.kind === "fixed" && p.from) offers = { "@type": "Offer", priceCurrency: "USD", price: p.from };
35
+ else if (p.kind === "starting_at" && p.from) {
36
+ offers = { "@type": "Offer", priceCurrency: "USD", price: p.from, priceSpecification: { "@type": "PriceSpecification", priceCurrency: "USD", minPrice: p.from } };
37
+ } else if (p.kind === "range" && p.from && p.to) {
38
+ offers = { "@type": "Offer", priceCurrency: "USD", priceSpecification: { "@type": "PriceSpecification", priceCurrency: "USD", minPrice: p.from, maxPrice: p.to } };
39
+ }
40
+ return {
41
+ "@context": "https://schema.org",
42
+ "@type": "Service",
43
+ name: service.name,
44
+ url,
45
+ ...(service.summary ? { description: service.summary } : {}),
46
+ ...(service.category ? { serviceType: service.category } : {}),
47
+ ...(service.photos.length ? { image: service.photos.map((ph) => ph.url) } : {}),
48
+ provider,
49
+ ...(a.city ? { areaServed: a.region ? `${a.city}, ${a.region}` : a.city } : {}),
50
+ ...(offers ? { offers } : {}),
51
+ };
52
+ }