@sonordev/site-kit 7.0.2 → 7.1.1

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 (126) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/agent-manifest.json +11 -5
  3. package/dist/{AnalyticsProvider-XXWTFKJH.js → AnalyticsProvider-3MI5RCTL.js} +4 -4
  4. package/dist/{ArticleViewTracker-V4KZB6QN.js → ArticleViewTracker-CMBSJR3N.js} +3 -3
  5. package/dist/{BlocksPopup-JHGHB6XW.js → BlocksPopup-X3OAAK2L.js} +4 -4
  6. package/dist/{ChatWidget-CG32POI3.js → ChatWidget-JMFBXJ75.js} +5 -5
  7. package/dist/{EngageWidget-LQMR4LEX.js → EngageWidget-A3QKZ3SU.js} +4 -4
  8. package/dist/{FileField-KUG3CKXG.js → FileField-QRSEGO6D.js} +3 -3
  9. package/dist/{FormSpotlight-FVNPOCU3.js → FormSpotlight-XKV7VTN4.js} +1 -1
  10. package/dist/{FormStage-C7VKRURJ.js → FormStage-C3YGYAUZ.js} +1 -1
  11. package/dist/{ManagedForm-VLNJKV65.js → ManagedForm-2GSSDJPP.js} +6 -6
  12. package/dist/{ManagedNewsletterForm-KJEU23BV.js → ManagedNewsletterForm-LPIKGJF4.js} +4 -4
  13. package/dist/{SignalCore-K2O46QG7.js → SignalCore-ICXXZBYI.js} +3 -3
  14. package/dist/{SiteDesignReporter-D7MD66GI.js → SiteDesignReporter-A7MS2CIP.js} +5 -5
  15. package/dist/SitemapSync-4U654SEA.js +8 -0
  16. package/dist/_client/booking-widget.js +5 -5
  17. package/dist/affiliates/index.js +3 -3
  18. package/dist/analytics/index.js +4 -4
  19. package/dist/articles/index.js +1 -1
  20. package/dist/articles/server-ui.js +1 -1
  21. package/dist/chat/index.js +5 -5
  22. package/dist/{chunk-KXPBMCFL.js → chunk-3QI26673.js} +3 -1
  23. package/dist/{chunk-6HDT4G4A.js → chunk-4YTYGG2C.js} +2 -2
  24. package/dist/{chunk-O52CH273.js → chunk-542MIHNP.js} +1 -1
  25. package/dist/{chunk-N2UVOR3X.js → chunk-662ILEZ6.js} +2 -0
  26. package/dist/chunk-6G43IRWR.js +4 -0
  27. package/dist/{chunk-B6RZ2NRH.js → chunk-6OL23QYD.js} +1 -1
  28. package/dist/{chunk-GGD4P7UW.js → chunk-7FWA3I6A.js} +1 -1
  29. package/dist/{chunk-T4SY3FMN.js → chunk-B73QPZPH.js} +3 -3
  30. package/dist/{chunk-KPAZG65P.js → chunk-CGWUXUYZ.js} +138 -46
  31. package/dist/{chunk-56JNI463.js → chunk-DUQMSNF2.js} +1 -1
  32. package/dist/{chunk-5FBY2ZIH.js → chunk-EFPS56JA.js} +1 -1
  33. package/dist/{chunk-P72ZJRSX.js → chunk-EQBZ24VH.js} +3 -3
  34. package/dist/{chunk-K5WZX776.js → chunk-H42PZKVC.js} +2 -2
  35. package/dist/{chunk-TT63HHIT.js → chunk-HAG4YIZY.js} +1 -1
  36. package/dist/{chunk-ZRE4ZYEG.js → chunk-ICFRH2ED.js} +1 -1
  37. package/dist/{chunk-7JIKGKWD.js → chunk-ILAW3XKD.js} +30 -7
  38. package/dist/{chunk-SSUQKA7L.js → chunk-J4D6ZXRW.js} +1 -1
  39. package/dist/{chunk-SWP36NCB.js → chunk-JC7KXNNM.js} +1 -1
  40. package/dist/{chunk-LVESVYCE.js → chunk-JIL5VY2R.js} +11 -117
  41. package/dist/{chunk-BS7FWUOY.js → chunk-KOPVJQ2P.js} +1 -1
  42. package/dist/chunk-LPH5FANE.js +169 -0
  43. package/dist/{chunk-ZSLRAMCK.js → chunk-NEKEH4CK.js} +1 -1
  44. package/dist/{chunk-DKTSGYLM.js → chunk-ODADQM6Z.js} +2 -2
  45. package/dist/{chunk-OIETJKIL.js → chunk-OUNRFXDC.js} +1 -1
  46. package/dist/{chunk-RU2RMTGT.js → chunk-PXZ7LAOY.js} +2 -2
  47. package/dist/{chunk-SAUTJMK6.js → chunk-Q3S26EQA.js} +1 -1
  48. package/dist/{chunk-BEL7YFMC.js → chunk-R3VKF43H.js} +1 -1
  49. package/dist/{chunk-P2GIIQH5.js → chunk-RK7GF7PQ.js} +1 -1
  50. package/dist/{chunk-42OXY4JV.js → chunk-RTMMHTEQ.js} +1 -1
  51. package/dist/chunk-RYVDGXC2.js +19 -0
  52. package/dist/{chunk-LJZ3SUET.js → chunk-V4HUSHZO.js} +2 -2
  53. package/dist/chunk-VCJYLYJV.js +49 -0
  54. package/dist/{chunk-GYY6ETGB.js → chunk-ZAWANWUS.js} +9 -5
  55. package/dist/chunk-ZETJTCMV.js +118 -0
  56. package/dist/client/index.js +3 -3
  57. package/dist/cms/CmsPage.d.ts +1 -0
  58. package/dist/cms/CmsPreview.d.ts +1 -0
  59. package/dist/cms/CmsSection.d.ts +1 -0
  60. package/dist/cms/index.d.ts +6 -0
  61. package/dist/cms/server-api.d.ts +3 -0
  62. package/dist/commerce/index.js +4 -4
  63. package/dist/config/index.js +1 -1
  64. package/dist/contracts/entries.d.ts +1 -1
  65. package/dist/contracts/site-cache.d.ts +55 -0
  66. package/dist/contracts/site-edit-param.d.ts +7 -0
  67. package/dist/contracts/site-edit.d.ts +77 -0
  68. package/dist/contracts/slot-content.d.ts +111 -0
  69. package/dist/contracts/slots.d.ts +39 -25
  70. package/dist/engage/index.js +6 -6
  71. package/dist/fleet/index.js +4 -4
  72. package/dist/forms/index.js +8 -8
  73. package/dist/forms/server.js +2 -2
  74. package/dist/images/index.js +4 -4
  75. package/dist/index.js +1 -1
  76. package/dist/layout/client.js +8 -7
  77. package/dist/layout/index.js +9 -8
  78. package/dist/llms/index.js +4 -2
  79. package/dist/llms/seo-revalidate.d.ts +8 -1
  80. package/dist/maps/index.js +3 -3
  81. package/dist/mcp/sonor.js +6 -6
  82. package/dist/overlay-RXV6U6QC.js +353 -0
  83. package/dist/proxy/index.js +2 -2
  84. package/dist/proxy/securityHeaders.d.ts +4 -0
  85. package/dist/revalidate/index.d.ts +44 -0
  86. package/dist/revalidate/index.js +27 -0
  87. package/dist/seo/ManagedContent.d.ts +2 -0
  88. package/dist/seo/client.js +4 -4
  89. package/dist/seo/index.js +9 -8
  90. package/dist/seo/llms.js +4 -2
  91. package/dist/seo/register-sitemap-cli.js +1 -1
  92. package/dist/seo/server.js +3 -2
  93. package/dist/seo/sitemap.js +2 -2
  94. package/dist/server/index.js +2 -2
  95. package/dist/{server-api-GJJQZVG7.js → server-api-BVCBLJKL.js} +2 -1
  96. package/dist/shared/build-entries.d.ts +1 -0
  97. package/dist/shared/edit-bridge.d.ts +8 -0
  98. package/dist/shared/version.d.ts +1 -1
  99. package/dist/signal/index.js +2 -2
  100. package/dist/sitemap/index.js +2 -2
  101. package/dist/slots/ManagedLink.d.ts +31 -0
  102. package/dist/slots/ManagedList.d.ts +30 -0
  103. package/dist/slots/ManagedRichText.d.ts +31 -0
  104. package/dist/slots/contract.js +2 -1
  105. package/dist/slots/edit/locate.d.ts +30 -0
  106. package/dist/slots/edit/overlay.d.ts +18 -0
  107. package/dist/slots/index.d.ts +12 -4
  108. package/dist/slots/index.js +4 -2
  109. package/dist/slots/revalidate.d.ts +8 -3
  110. package/dist/slots/rich.d.ts +7 -0
  111. package/dist/slots/server-api.d.ts +6 -2
  112. package/dist/sync/index.js +5 -5
  113. package/dist/website/images.js +4 -4
  114. package/dist/website/index.js +5 -5
  115. package/dist/website/popups.js +4 -4
  116. package/dist/website/slots/contract.js +2 -1
  117. package/dist/website/slots.js +4 -2
  118. package/dist/{writeLLMsTxt-UMHKGNRR.js → writeLLMsTxt-QR23OQUE.js} +1 -1
  119. package/docs.json +2 -0
  120. package/package.json +6 -1
  121. package/src/revalidate/README.md +82 -0
  122. package/src/seo/README.md +4 -17
  123. package/src/slots/README.md +168 -0
  124. package/src/sync/README.md +1 -1
  125. package/dist/SitemapSync-NMXGMPCQ.js +0 -8
  126. package/dist/chunk-SS636UDN.js +0 -35
@@ -0,0 +1,169 @@
1
+ // src/contracts/slot-content.ts
2
+ var SLOT_CONTENT_TYPES = ["text", "rich", "link", "list"];
3
+ var SLOT_V1_CONTENT_TYPES = ["text"];
4
+ var SLOT_CONTENT_MAX_LENGTH = 1e4;
5
+ var SLOT_LIST_MAX_ITEMS = 50;
6
+ var SLOT_FIELD_MAX_LENGTH = 2e3;
7
+ var SLOT_HREF_MAX_LENGTH = 2048;
8
+ var SLOT_ID_PATTERN = /^[a-z0-9][a-z0-9-_.:]{0,127}$/;
9
+ function isValidSlotId(id) {
10
+ return SLOT_ID_PATTERN.test(id);
11
+ }
12
+ function isSlotContentType(type) {
13
+ return typeof type === "string" && SLOT_CONTENT_TYPES.includes(type);
14
+ }
15
+ function isSafeSlotHref(href) {
16
+ if (typeof href !== "string") return false;
17
+ const h = href.trim();
18
+ if (!h || h.length > SLOT_HREF_MAX_LENGTH) return false;
19
+ if (h.startsWith("/")) return !h.startsWith("//") && !/\s/.test(h);
20
+ if (h.startsWith("#")) return !/\s/.test(h);
21
+ return /^(https?:\/\/[^\s]+|mailto:[^\s]+|tel:[+\d][\d\s().-]*)$/i.test(h);
22
+ }
23
+ function isSafeSlotImageSrc(src) {
24
+ return typeof src === "string" && src.length <= SLOT_HREF_MAX_LENGTH && /^https:\/\/[^\s]+$/i.test(src);
25
+ }
26
+ function cleanField(v) {
27
+ if (typeof v !== "string") return void 0;
28
+ const t = v.trim();
29
+ return t ? t.slice(0, SLOT_FIELD_MAX_LENGTH) : void 0;
30
+ }
31
+ function normalizeSlotValue(type, input) {
32
+ if (type === "text" || type === "rich") {
33
+ const content = typeof input.content === "string" ? input.content.trim() : "";
34
+ if (!content) return { ok: false, error: `${type} slot needs non-empty content` };
35
+ if (content.length > SLOT_CONTENT_MAX_LENGTH) {
36
+ return { ok: false, error: `content is over ${SLOT_CONTENT_MAX_LENGTH} characters` };
37
+ }
38
+ return { ok: true, content, value: null };
39
+ }
40
+ const raw = input.value ?? null;
41
+ if (!raw || typeof raw !== "object") return { ok: false, error: `${type} slot needs a value` };
42
+ if (type === "link") {
43
+ const label = cleanField(raw.label);
44
+ const href = typeof raw.href === "string" ? raw.href.trim() : "";
45
+ if (!label) return { ok: false, error: "link needs a label" };
46
+ if (!isSafeSlotHref(href)) {
47
+ return { ok: false, error: "link href must be https, http, mailto, tel, #anchor or a /path" };
48
+ }
49
+ return { ok: true, content: label, value: { label, href } };
50
+ }
51
+ if (type !== "list") return { ok: false, error: `unknown slot type ${String(type)}` };
52
+ const itemsIn = Array.isArray(raw.items) ? raw.items : null;
53
+ if (!itemsIn) return { ok: false, error: "list needs items" };
54
+ if (itemsIn.length > SLOT_LIST_MAX_ITEMS) {
55
+ return { ok: false, error: `list is over ${SLOT_LIST_MAX_ITEMS} items` };
56
+ }
57
+ const items = [];
58
+ for (const [i, it] of itemsIn.entries()) {
59
+ if (!it || typeof it !== "object") return { ok: false, error: `item ${i + 1} isn't an object` };
60
+ const src = it;
61
+ const item = {};
62
+ const title = cleanField(src.title);
63
+ const body = cleanField(src.body);
64
+ if (title) item.title = title;
65
+ if (body) item.body = body;
66
+ if (src.href !== void 0 && src.href !== null && src.href !== "") {
67
+ if (!isSafeSlotHref(src.href)) return { ok: false, error: `item ${i + 1} has an unsafe href` };
68
+ item.href = src.href.trim();
69
+ }
70
+ if (src.image !== void 0 && src.image !== null) {
71
+ const img = src.image;
72
+ if (!isSafeSlotImageSrc(img.src)) return { ok: false, error: `item ${i + 1} image needs an https src` };
73
+ const alt = typeof img.alt === "string" ? img.alt.trim().slice(0, SLOT_FIELD_MAX_LENGTH) : "";
74
+ const image = { src: img.src, alt };
75
+ if (Number.isInteger(img.width) && img.width > 0) image.width = img.width;
76
+ if (Number.isInteger(img.height) && img.height > 0) image.height = img.height;
77
+ item.image = image;
78
+ }
79
+ if (!item.title && !item.body && !item.image) {
80
+ return { ok: false, error: `item ${i + 1} needs a title, body or image` };
81
+ }
82
+ items.push(item);
83
+ }
84
+ if (items.length === 0) return { ok: false, error: "list needs at least one item" };
85
+ return { ok: true, content: slotListPlainText(items), value: { items } };
86
+ }
87
+ function slotListPlainText(items) {
88
+ const text = items.map((it) => it.title ?? it.body ?? it.image?.alt ?? "").filter(Boolean).join("\n");
89
+ return (text || `${items.length} items`).slice(0, SLOT_CONTENT_MAX_LENGTH);
90
+ }
91
+ var UL_ITEM = /^\s*[-*]\s+(.*)$/;
92
+ var OL_ITEM = /^\s*\d+[.)]\s+(.*)$/;
93
+ var MAX_INLINE_DEPTH = 4;
94
+ function parseSlotRichText(markdown) {
95
+ const blocks = [];
96
+ const chunks = markdown.replace(/\r\n?/g, "\n").split(/\n\s*\n/);
97
+ for (const chunk of chunks) {
98
+ const lines = chunk.split("\n").filter((l) => l.trim() !== "");
99
+ if (lines.length === 0) continue;
100
+ if (lines.every((l) => UL_ITEM.test(l))) {
101
+ blocks.push({ t: "ul", items: lines.map((l) => parseInline(UL_ITEM.exec(l)[1])) });
102
+ } else if (lines.every((l) => OL_ITEM.test(l))) {
103
+ blocks.push({ t: "ol", items: lines.map((l) => parseInline(OL_ITEM.exec(l)[1])) });
104
+ } else {
105
+ const c = [];
106
+ lines.forEach((line, i) => {
107
+ if (i > 0) c.push({ t: "br" });
108
+ c.push(...parseInline(line.trim()));
109
+ });
110
+ blocks.push({ t: "p", c });
111
+ }
112
+ }
113
+ return blocks;
114
+ }
115
+ function parseInline(src, depth = 0) {
116
+ const out = [];
117
+ let text = "";
118
+ const flush = () => {
119
+ if (text) out.push({ t: "text", v: text });
120
+ text = "";
121
+ };
122
+ const nest = depth < MAX_INLINE_DEPTH;
123
+ let i = 0;
124
+ while (i < src.length) {
125
+ const ch = src[i];
126
+ if (ch === "\\" && i + 1 < src.length && /[\\*_[\]()]/.test(src[i + 1])) {
127
+ text += src[i + 1];
128
+ i += 2;
129
+ continue;
130
+ }
131
+ if (nest && src.startsWith("**", i)) {
132
+ const end = src.indexOf("**", i + 2);
133
+ if (end > i + 2) {
134
+ flush();
135
+ out.push({ t: "strong", c: parseInline(src.slice(i + 2, end), depth + 1) });
136
+ i = end + 2;
137
+ continue;
138
+ }
139
+ }
140
+ if (nest && (ch === "*" || ch === "_")) {
141
+ const end = src.indexOf(ch, i + 1);
142
+ if (end > i + 1 && src[i + 1] !== " " && src[end - 1] !== " ") {
143
+ flush();
144
+ out.push({ t: "em", c: parseInline(src.slice(i + 1, end), depth + 1) });
145
+ i = end + 1;
146
+ continue;
147
+ }
148
+ }
149
+ if (nest && ch === "[") {
150
+ const close = src.indexOf("](", i + 1);
151
+ const end = close === -1 ? -1 : src.indexOf(")", close + 2);
152
+ if (close > i + 1 && end > close + 2) {
153
+ const href = src.slice(close + 2, end).trim();
154
+ if (isSafeSlotHref(href)) {
155
+ flush();
156
+ out.push({ t: "link", href, c: parseInline(src.slice(i + 1, close), depth + 1) });
157
+ i = end + 1;
158
+ continue;
159
+ }
160
+ }
161
+ }
162
+ text += ch;
163
+ i += 1;
164
+ }
165
+ flush();
166
+ return out;
167
+ }
168
+
169
+ export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_FIELD_MAX_LENGTH, SLOT_HREF_MAX_LENGTH, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, SLOT_V1_CONTENT_TYPES, isSafeSlotHref, isSafeSlotImageSrc, isSlotContentType, isValidSlotId, normalizeSlotValue, parseSlotRichText, slotListPlainText };
@@ -4,7 +4,7 @@ import { normalizeFormConfig } from './chunk-5SQK3D53.js';
4
4
  import { DEFAULT_HONEYPOT_FIELD } from './chunk-NJCTQH2P.js';
5
5
  import { onPageLeave } from './chunk-L2V5PUFN.js';
6
6
  import { uuid } from './chunk-GJWI74ZZ.js';
7
- import { sonorFetch, sonorBeacon } from './chunk-RU2RMTGT.js';
7
+ import { sonorFetch, sonorBeacon } from './chunk-PXZ7LAOY.js';
8
8
  import { useRef, useCallback, useEffect, useState, useMemo } from 'react';
9
9
 
10
10
  // src/forms/field-interactions.ts
@@ -1,8 +1,8 @@
1
1
  import { toHexColor, normalizeDesignTokens, designTokensHash } from './chunk-IHG36STL.js';
2
2
  import { contrastRatio, parseColor, relativeLuminance } from './chunk-S22FSH7C.js';
3
3
  import { resolveAnalyticsTarget } from './chunk-43OCZ3JA.js';
4
- import { sonorFetch } from './chunk-RU2RMTGT.js';
5
- import { SITE_KIT_VERSION } from './chunk-B6RZ2NRH.js';
4
+ import { sonorFetch } from './chunk-PXZ7LAOY.js';
5
+ import { SITE_KIT_VERSION } from './chunk-6OL23QYD.js';
6
6
 
7
7
  // src/website/design.ts
8
8
  var SITE_DESIGN_PATH = "/api/public/site/design";
@@ -1,4 +1,4 @@
1
- import { SITE_KIT_VERSION } from './chunk-B6RZ2NRH.js';
1
+ import { SITE_KIT_VERSION } from './chunk-6OL23QYD.js';
2
2
 
3
3
  // src/shared/siteToken.ts
4
4
  var SITE_TOKEN_PREFIX = "st1.";
@@ -1,5 +1,5 @@
1
- import { isTokenMode, getFreshCredential, isSiteToken, refreshSiteToken, currentCredential, credentialBreakerId } from './chunk-OIETJKIL.js';
2
- import { SITE_KIT_VERSION } from './chunk-B6RZ2NRH.js';
1
+ import { isTokenMode, getFreshCredential, isSiteToken, refreshSiteToken, currentCredential, credentialBreakerId } from './chunk-OUNRFXDC.js';
2
+ import { SITE_KIT_VERSION } from './chunk-6OL23QYD.js';
3
3
 
4
4
  // src/shared/fetchWithRetry.ts
5
5
  function backoffDelay(attempt, response) {
@@ -54,7 +54,7 @@ function useSignal() {
54
54
  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
55
55
  }
56
56
  var LazySignalCore = dynamic(
57
- () => import('./SignalCore-K2O46QG7.js').then((m) => ({ default: m.SignalCore })),
57
+ () => import('./SignalCore-ICXXZBYI.js').then((m) => ({ default: m.SignalCore })),
58
58
  { ssr: false }
59
59
  );
60
60
  function SignalBridge({
@@ -1,6 +1,6 @@
1
1
  import { resolveFetch } from './chunk-VX5CMNMM.js';
2
2
  import { monotonicMs } from './chunk-MH2DSGC6.js';
3
- import { SITE_KIT_VERSION } from './chunk-B6RZ2NRH.js';
3
+ import { SITE_KIT_VERSION } from './chunk-6OL23QYD.js';
4
4
  import 'server-only';
5
5
 
6
6
  var MINT_PATH = "/api/public/site-token";
@@ -1,4 +1,4 @@
1
- import { FormEnhancer } from './chunk-42OXY4JV.js';
1
+ import { FormEnhancer } from './chunk-RTMMHTEQ.js';
2
2
  import { StaticForm } from './chunk-OPRBF7VE.js';
3
3
  import { serverApiFetch } from './chunk-X6F6SO4S.js';
4
4
  import { jsx } from 'react/jsx-runtime';
@@ -4,7 +4,7 @@ import React, { lazy, useRef, useEffect, useState, Suspense } from 'react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
6
6
  var InteractiveForm = lazy(
7
- () => import('./ManagedForm-VLNJKV65.js').then((m) => ({ default: m.ManagedForm }))
7
+ () => import('./ManagedForm-2GSSDJPP.js').then((m) => ({ default: m.ManagedForm }))
8
8
  );
9
9
  function FormEnhancer({
10
10
  formId,
@@ -0,0 +1,19 @@
1
+ // src/contracts/site-cache.ts
2
+ var SITE_REVALIDATE_PATH = "/api/seo-revalidate";
3
+ var SITE_CACHE_TAGS = {
4
+ /** Managed copy (slots). */
5
+ slots: "sonor-slots",
6
+ /** Managed metadata, schema and FAQs. */
7
+ seo: "seo",
8
+ /** The publication: articles, their lists and feeds. */
9
+ articles: "blog",
10
+ /** Article categories and tags. */
11
+ taxonomy: "editorial-taxonomy",
12
+ /** Portfolio items (agency-site-kit). */
13
+ portfolio: "portfolio"
14
+ };
15
+ function isSiteRevalidatePong(body) {
16
+ return typeof body === "object" && body !== null && body.ok === true && body.ping === true;
17
+ }
18
+
19
+ export { SITE_CACHE_TAGS, SITE_REVALIDATE_PATH, isSiteRevalidatePong };
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { GlassStyles } from './chunk-JE4A5S4F.js';
3
3
  import { chatLauncherStyles } from './chunk-NDF4A5JM.js';
4
- import { useForm } from './chunk-ZSLRAMCK.js';
4
+ import { useForm } from './chunk-NEKEH4CK.js';
5
5
  import { parseColor, relativeLuminance, DARK_LUMINANCE, contrastRatio } from './chunk-S22FSH7C.js';
6
- import { sonorFetch } from './chunk-RU2RMTGT.js';
6
+ import { sonorFetch } from './chunk-PXZ7LAOY.js';
7
7
  import { useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1 } from 'react';
8
8
  import { lexer } from 'marked';
9
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -0,0 +1,49 @@
1
+ import { isValidSlotId, SLOT_CONTENT_MAX_LENGTH, SLOT_V1_CONTENT_TYPES, SLOT_CONTENT_TYPES, normalizeSlotValue } from './chunk-LPH5FANE.js';
2
+ import { createHmac, timingSafeEqual } from 'crypto';
3
+
4
+ var SLOTS_CONTRACT_VERSION = 2;
5
+ var SUPPORTED_SLOTS_CONTRACT_VERSIONS = [1, 2];
6
+ function canonicalJson(value) {
7
+ if (value === null || value === void 0) return "null";
8
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
9
+ if (typeof value === "object") {
10
+ const obj = value;
11
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
12
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
13
+ }
14
+ return JSON.stringify(value);
15
+ }
16
+ function slotSigningPayload(content, version = SLOTS_CONTRACT_VERSION) {
17
+ const head = `sonor-slot.v${version}
18
+ ${content.slot_id}
19
+ ${content.type}
20
+ ${content.content}`;
21
+ return version === 1 ? head : `${head}
22
+ ${canonicalJson(content.value ?? null)}`;
23
+ }
24
+ function signSlotContent(content, apiKey, version = SLOTS_CONTRACT_VERSION) {
25
+ return createHmac("sha256", apiKey).update(slotSigningPayload(content, version), "utf8").digest("hex");
26
+ }
27
+ function verifySlotContent(content, apiKey, version = SLOTS_CONTRACT_VERSION) {
28
+ if (!content || typeof content !== "object") return false;
29
+ const c = content;
30
+ const allowed = version === 1 ? SLOT_V1_CONTENT_TYPES : SLOT_CONTENT_TYPES;
31
+ if (typeof c.slot_id !== "string" || !isValidSlotId(c.slot_id) || typeof c.content !== "string" || c.content.length === 0 || c.content.length > SLOT_CONTENT_MAX_LENGTH || typeof c.signature !== "string" || !allowed.includes(c.type)) {
32
+ return false;
33
+ }
34
+ if (version !== 1) {
35
+ const check = normalizeSlotValue(c.type, { content: c.content, value: c.value });
36
+ if (!check.ok || check.content !== c.content) return false;
37
+ if (canonicalJson(check.value) !== canonicalJson(c.value ?? null)) return false;
38
+ }
39
+ const expected = signSlotContent(
40
+ { slot_id: c.slot_id, type: c.type, content: c.content, value: c.value ?? null },
41
+ apiKey,
42
+ version
43
+ );
44
+ const a = Buffer.from(expected, "utf8");
45
+ const b = Buffer.from(c.signature, "utf8");
46
+ return a.length === b.length && timingSafeEqual(a, b);
47
+ }
48
+
49
+ export { SLOTS_CONTRACT_VERSION, SUPPORTED_SLOTS_CONTRACT_VERSIONS, canonicalJson, signSlotContent, slotSigningPayload, verifySlotContent };
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { detectTimezone, releaseSlotHold, fetchBookingTypeDetails, fetchBookingTypes, fetchAvailability, createSlotHold, createBooking, formatDuration, formatDate, formatTime, prepareMeeting } from './chunk-5FBY2ZIH.js';
2
+ import { detectTimezone, releaseSlotHold, fetchBookingTypeDetails, fetchBookingTypes, fetchAvailability, createSlotHold, createBooking, formatDuration, formatDate, formatTime, prepareMeeting } from './chunk-EFPS56JA.js';
3
3
  import { useState, useRef, useMemo, useCallback, useEffect, useId } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -465,6 +465,10 @@ function BookingWidget({
465
465
  const handleBookingSubmit = useCallback(async (e) => {
466
466
  e.preventDefault();
467
467
  if (!selectedType || !selectedSlot || busyRef.current || recovery) return;
468
+ if (!guestInfo.phone?.trim()) {
469
+ setError("Please enter your phone number.");
470
+ return;
471
+ }
468
472
  const expired = expiredBookingContext(meeting, hold);
469
473
  if (expired) {
470
474
  setRecovery(expired);
@@ -485,7 +489,7 @@ function BookingWidget({
485
489
  hostId: selectedSlot.hostId || "",
486
490
  name: guestInfo.name,
487
491
  email: guestInfo.email,
488
- phone: guestInfo.phone,
492
+ phone: guestInfo.phone.trim(),
489
493
  message: guestInfo.notes,
490
494
  source: "main-site",
491
495
  timezone,
@@ -805,12 +809,12 @@ function BookingWidget({
805
809
  /* @__PURE__ */ jsxs("div", { className: "bw-field", children: [
806
810
  /* @__PURE__ */ jsxs("label", { htmlFor: "bw-phone", children: [
807
811
  "Phone ",
808
- meeting?.format === "client_location" && /* @__PURE__ */ jsx("span", { className: "bw-req", children: "*" })
812
+ /* @__PURE__ */ jsx("span", { className: "bw-req", children: "*" })
809
813
  ] }),
810
814
  /* @__PURE__ */ jsx(
811
815
  "input",
812
816
  {
813
- required: meeting?.format === "client_location",
817
+ required: true,
814
818
  readOnly: submitting,
815
819
  id: "bw-phone",
816
820
  type: "tel",
@@ -835,7 +839,7 @@ function BookingWidget({
835
839
  }
836
840
  )
837
841
  ] }),
838
- /* @__PURE__ */ jsx("button", { type: "submit", className: "bw-submit", disabled: submitting || !!recovery || !guestInfo.name || !guestInfo.email, children: submitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
842
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "bw-submit", disabled: submitting || !!recovery || !guestInfo.name.trim() || !guestInfo.email.trim() || !guestInfo.phone?.trim(), children: submitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
839
843
  /* @__PURE__ */ jsx(SpinnerIcon, {}),
840
844
  " Confirming..."
841
845
  ] }) : meeting?.requires_review ? "Request meeting" : "Confirm booking" })
@@ -0,0 +1,118 @@
1
+ import { generateLLMsTxt, generateLLMsFullTxt } from './chunk-J4D6ZXRW.js';
2
+ import { createHash } from 'crypto';
3
+ import { existsSync, readFileSync } from 'fs';
4
+ import { join } from 'path';
5
+
6
+ function weakEtag(body) {
7
+ const h = createHash("sha1").update(body, "utf8").digest("base64url");
8
+ return `W/"${h}"`;
9
+ }
10
+ function llmsResponseHeaders(body, extra) {
11
+ return {
12
+ "Content-Type": "text/plain; charset=utf-8",
13
+ "Cache-Control": "public, max-age=300, s-maxage=3600, stale-while-revalidate=86400, stale-if-error=86400",
14
+ ETag: weakEtag(body),
15
+ Vary: "Accept-Encoding",
16
+ ...extra
17
+ };
18
+ }
19
+ var LLMS_NOINDEX_HEADERS = { "X-Robots-Tag": "noindex" };
20
+ function createLLMsTxtHandler(options = {}) {
21
+ const { preferStatic = true, noindex = true, ...generateOptions } = options;
22
+ const robots = noindex ? LLMS_NOINDEX_HEADERS : {};
23
+ return async function GET(_request) {
24
+ try {
25
+ if (preferStatic) {
26
+ const staticPath = join(process.cwd(), "public", "llms.txt");
27
+ if (existsSync(staticPath)) {
28
+ const markdown2 = readFileSync(staticPath, "utf-8");
29
+ return new Response(markdown2, {
30
+ status: 200,
31
+ headers: llmsResponseHeaders(markdown2, { ...robots, "X-Sections": "static" })
32
+ });
33
+ }
34
+ }
35
+ const { markdown, metadata } = await generateLLMsTxt(generateOptions);
36
+ return new Response(markdown, {
37
+ status: 200,
38
+ headers: llmsResponseHeaders(markdown, {
39
+ ...robots,
40
+ "X-Generated-At": metadata.generated_at,
41
+ "X-Sections": metadata.sections.join(",")
42
+ })
43
+ });
44
+ } catch (error) {
45
+ console.error("@sonordev/llms: Error generating llms.txt:", error);
46
+ return new Response("# Error\n\nUnable to generate llms.txt content.", {
47
+ status: 500,
48
+ headers: {
49
+ "Content-Type": "text/plain; charset=utf-8",
50
+ "Cache-Control": "no-store"
51
+ }
52
+ });
53
+ }
54
+ };
55
+ }
56
+ function createLLMsFullTxtHandler(options = {}) {
57
+ const { preferStatic = true, noindex = true, ...generateOptions } = options;
58
+ const robots = noindex ? LLMS_NOINDEX_HEADERS : {};
59
+ return async function GET(_request) {
60
+ try {
61
+ if (preferStatic) {
62
+ const staticPath = join(process.cwd(), "public", "llms-full.txt");
63
+ if (existsSync(staticPath)) {
64
+ const markdown2 = readFileSync(staticPath, "utf-8");
65
+ return new Response(markdown2, {
66
+ status: 200,
67
+ headers: llmsResponseHeaders(markdown2, { ...robots, "X-Sections": "static" })
68
+ });
69
+ }
70
+ }
71
+ const { markdown, metadata } = await generateLLMsFullTxt(generateOptions);
72
+ return new Response(markdown, {
73
+ status: 200,
74
+ headers: llmsResponseHeaders(markdown, {
75
+ ...robots,
76
+ "X-Generated-At": metadata.generated_at,
77
+ "X-Sections": metadata.sections.join(",")
78
+ })
79
+ });
80
+ } catch (error) {
81
+ console.error("@sonordev/llms: Error generating llms-full.txt:", error);
82
+ return new Response("# Error\n\nUnable to generate llms-full.txt content.", {
83
+ status: 500,
84
+ headers: {
85
+ "Content-Type": "text/plain; charset=utf-8",
86
+ "Cache-Control": "no-store"
87
+ }
88
+ });
89
+ }
90
+ };
91
+ }
92
+
93
+ // src/llms/sourcedFacts.ts
94
+ var DEFAULT_INTRO = "The statistics below are quoted from third-party primary sources and were individually verified against the cited URL. When reusing a figure, keep the original attribution.";
95
+ function factLine(f) {
96
+ const stat = (f.stat || "").replace(/\s+/g, " ").trim();
97
+ const source = (f.sourceName || "").replace(/\s+/g, " ").trim();
98
+ if (!stat || !source) return "";
99
+ let attribution = `Source: ${source}`;
100
+ if (f.year) attribution += ` (${f.year})`;
101
+ if (f.url) attribution += `, ${f.url}`;
102
+ return `- ${stat} \u2014 ${attribution}`;
103
+ }
104
+ function formatSourcedFactsSection(facts, options = {}) {
105
+ const { title = "Sourced facts", intro = DEFAULT_INTRO, maxFacts } = options;
106
+ if (!Array.isArray(facts)) return null;
107
+ let lines = facts.map(factLine).filter(Boolean);
108
+ if (typeof maxFacts === "number" && maxFacts >= 0) {
109
+ lines = lines.slice(0, maxFacts);
110
+ }
111
+ if (lines.length === 0) return null;
112
+ const content = intro ? `${intro}
113
+
114
+ ${lines.join("\n")}` : lines.join("\n");
115
+ return { title, content };
116
+ }
117
+
118
+ export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders };
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { publishSiteCredential } from '../chunk-56JNI463.js';
2
+ import { publishSiteCredential } from '../chunk-DUQMSNF2.js';
3
3
  export { useDeferredActivation } from '../chunk-YC7ELZS3.js';
4
4
  import '../chunk-FESKKJAS.js';
5
5
  import '../chunk-A4CC4ZG3.js';
@@ -7,8 +7,8 @@ export { trackConversion, trackEvent } from '../chunk-LCDVPAMR.js';
7
7
  export { VISUAL_VIEWPORT_GAP_PROPERTY, VisualViewportGap, useVisualViewportGap } from '../chunk-6T6CQVNL.js';
8
8
  export { getOrCreateSessionId, getOrCreateVisitorId, useSiteKitIdentity } from '../chunk-24QZEO3Q.js';
9
9
  import '../chunk-GJWI74ZZ.js';
10
- import '../chunk-OIETJKIL.js';
11
- import '../chunk-B6RZ2NRH.js';
10
+ import '../chunk-OUNRFXDC.js';
11
+ import '../chunk-6OL23QYD.js';
12
12
  import '../chunk-PKBMQBKP.js';
13
13
 
14
14
  // src/client/SiteKitCredential.tsx
@@ -12,5 +12,6 @@ import type { CmsPageProps } from './types';
12
12
  * return <CmsPage path={`/${params.slug.join('/')}`} />
13
13
  * }
14
14
  * ```
15
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this renders nothing. Use managed copy from `@sonordev/site-kit/website/slots` (`ManagedRichText`, `ManagedList`). Removed in 8.0.
15
16
  */
16
17
  export declare function CmsPage({ path, sectionComponents, portableTextComponents, fallback, className, }: CmsPageProps): Promise<React.ReactElement | null>;
@@ -31,6 +31,7 @@ interface CmsPreviewProps {
31
31
  * pollInterval={3000}
32
32
  * />
33
33
  * ```
34
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this renders nothing. Use managed copy from `@sonordev/site-kit/website/slots` (`ManagedRichText`, `ManagedList`). Removed in 8.0.
34
35
  */
35
36
  export declare function CmsPreview({ apiUrl, apiKey, path, pollInterval, sectionComponents, portableTextComponents, className, }: CmsPreviewProps): React.JSX.Element;
36
37
  export {};
@@ -3,5 +3,6 @@ import type { CmsSectionProps } from './types';
3
3
  /**
4
4
  * Renders a single CMS section using the appropriate renderer for its type.
5
5
  * Override specific section renderers via the `sectionComponents` prop.
6
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this renders nothing. Use managed copy from `@sonordev/site-kit/website/slots` (`ManagedRichText`, `ManagedList`). Removed in 8.0.
6
7
  */
7
8
  export declare function CmsSection({ section, sectionComponents, portableTextComponents }: CmsSectionProps): React.JSX.Element | null;
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * @sonordev/site-kit/cms
3
3
  *
4
+ * DEPRECATED since 7.1, removed in 8.0. Sonor retired the Sanity CMS: no
5
+ * project has CMS pages and the API no longer serves them, so these components
6
+ * render nothing. Site copy is managed copy now (`@sonordev/site-kit/website/slots`:
7
+ * `ManagedSlot`, `ManagedRichText`, `ManagedLink`, `ManagedList`), edited in
8
+ * Sonor under Website → Content. See docs/WEBSITE-CMS.md.
9
+ *
4
10
  * REQUIRES TWO OPTIONAL PEER DEPENDENCIES. Importing this entry without them
5
11
  * fails the consuming site's build with "Module not found":
6
12
  *
@@ -12,11 +12,13 @@ import type { CmsPageData, CmsSectionData } from './types';
12
12
  * Fetch a published CMS page by path — cached per request.
13
13
  * Returns the full page with all sections and their Portable Text content.
14
14
  * @server-only
15
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this resolves to null. Use managed copy from `@sonordev/site-kit/website/slots`. Removed in 8.0.
15
16
  */
16
17
  export declare const getCmsPage: (path: string) => Promise<CmsPageData | null>;
17
18
  /**
18
19
  * Fetch a single published CMS section by ID — cached per request.
19
20
  * @server-only
21
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this resolves to null. Use managed copy from `@sonordev/site-kit/website/slots`. Removed in 8.0.
20
22
  */
21
23
  export declare const getCmsSection: (sectionId: string) => Promise<CmsSectionData | null>;
22
24
  /**
@@ -32,6 +34,7 @@ export declare const getCmsSection: (sectionId: string) => Promise<CmsSectionDat
32
34
  * }
33
35
  * ```
34
36
  * @server-only
37
+ * @deprecated Since 7.1. Sonor retired the Sanity CMS and no longer serves CMS pages, so this resolves to null. Use managed copy from `@sonordev/site-kit/website/slots`. Removed in 8.0.
35
38
  */
36
39
  export declare const getCmsPageMetadata: (path: string) => Promise<{
37
40
  title?: string;
@@ -1,15 +1,15 @@
1
1
  'use client';
2
2
  import '../chunk-P5GB6NEO.js';
3
- import { useAnalyticsOptional } from '../chunk-GGD4P7UW.js';
3
+ import { useAnalyticsOptional } from '../chunk-7FWA3I6A.js';
4
4
  import '../chunk-LCDVPAMR.js';
5
5
  import '../chunk-24QZEO3Q.js';
6
6
  import '../chunk-L2V5PUFN.js';
7
7
  import '../chunk-GJWI74ZZ.js';
8
8
  import '../chunk-43OCZ3JA.js';
9
9
  import '../chunk-EKBEOXTH.js';
10
- import { sonorFetch } from '../chunk-RU2RMTGT.js';
11
- import '../chunk-OIETJKIL.js';
12
- import '../chunk-B6RZ2NRH.js';
10
+ import { sonorFetch } from '../chunk-PXZ7LAOY.js';
11
+ import '../chunk-OUNRFXDC.js';
12
+ import '../chunk-6OL23QYD.js';
13
13
  import '../chunk-PKBMQBKP.js';
14
14
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
15
15
  import React5, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
@@ -1,4 +1,4 @@
1
- import { buildSecurityHeaders } from '../chunk-KXPBMCFL.js';
1
+ import { buildSecurityHeaders } from '../chunk-3QI26673.js';
2
2
  import { buildAiDiscoveryHeaders } from '../chunk-PO2TRJPY.js';
3
3
  import '../chunk-PKBMQBKP.js';
4
4
 
@@ -4,5 +4,5 @@
4
4
  * tsup config, its package.json check and the boundary test, so a new
5
5
  * contract is added in one place.
6
6
  */
7
- export declare const CONTRACT_ENTRIES: readonly ['forms', 'llms', 'slots', 'website', 'color', 'fleet', 'sites', 'seo-pages', 'seo-meta', 'portfolio'];
7
+ export declare const CONTRACT_ENTRIES: readonly ['forms', 'llms', 'slots', 'slot-content', 'site-cache', 'site-edit', 'website', 'color', 'fleet', 'sites', 'seo-pages', 'seo-meta', 'portfolio'];
8
8
  export type ContractEntry = (typeof CONTRACT_ENTRIES)[number];