@scalemule/news-publication 0.0.11 → 0.0.13

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.
@@ -9,6 +9,8 @@ var COOKIE_CONSENT_KEY = "sm_cookie_consent_v1";
9
9
  var COOKIE_CONSENT_BACKUP_KEY = "napsite_cookie_consent_v1";
10
10
  var COOKIE_CONSENT_EVENT = "scalemule-cookie-consent-change";
11
11
  var COOKIE_CONSENT_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
12
+ var DEFAULT_CONSENT_HUB_URL = "https://bayareachronicle.com/consent-bridge.html";
13
+ var NETWORK_CONSENT_STORAGE_KEY = "sm_network_consent_status";
12
14
  var DEFAULT_PREFERENCES_REJECTED = {
13
15
  performance: false,
14
16
  functional: false,
@@ -53,6 +55,235 @@ function getCookieConsent() {
53
55
  }
54
56
  return memoryConsent;
55
57
  }
58
+ var EU_EEA_UK_COUNTRIES = /* @__PURE__ */ new Set([
59
+ "AT",
60
+ "BE",
61
+ "BG",
62
+ "HR",
63
+ "CY",
64
+ "CZ",
65
+ "DK",
66
+ "EE",
67
+ "FI",
68
+ "FR",
69
+ "DE",
70
+ "GR",
71
+ "HU",
72
+ "IE",
73
+ "IT",
74
+ "LV",
75
+ "LT",
76
+ "LU",
77
+ "MT",
78
+ "NL",
79
+ "PL",
80
+ "PT",
81
+ "RO",
82
+ "SK",
83
+ "SI",
84
+ "ES",
85
+ "SE",
86
+ "IS",
87
+ "LI",
88
+ "NO",
89
+ "GB",
90
+ "CH"
91
+ ]);
92
+ function isGeoConsentRequired(countryCode) {
93
+ if (countryCode && typeof countryCode === "string") {
94
+ const code = countryCode.trim().toUpperCase();
95
+ if (code.length === 2) {
96
+ return EU_EEA_UK_COUNTRIES.has(code);
97
+ }
98
+ }
99
+ if (typeof window !== "undefined") {
100
+ try {
101
+ const cached = readCookie("sm_geo_country") || window.localStorage?.getItem("sm_geo_country");
102
+ if (cached && typeof cached === "string" && cached.length === 2) {
103
+ return EU_EEA_UK_COUNTRIES.has(cached.toUpperCase());
104
+ }
105
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
106
+ if (tz) {
107
+ if (/^(America|US|Canada|Pacific)\//i.test(tz)) {
108
+ return false;
109
+ }
110
+ if (/^(Europe|Atlantic\/Reykjavik|Atlantic\/Canary|Atlantic\/Faeroe|Atlantic\/Madeira|Atlantic\/Azores|WET|CET|EET|GMT)/i.test(tz)) {
111
+ return true;
112
+ }
113
+ }
114
+ } catch {
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ function checkUrlConsentBridge() {
120
+ if (typeof window === "undefined") return false;
121
+ try {
122
+ const url = new URL(window.location.href);
123
+ const param = url.searchParams.get("sm_consent");
124
+ if (!param) return false;
125
+ if (param === "accepted" || param === "1" || param === "all") {
126
+ acceptAllCookies();
127
+ } else if (param === "rejected" || param === "0" || param === "essential") {
128
+ rejectOptionalCookies();
129
+ } else {
130
+ return false;
131
+ }
132
+ url.searchParams.delete("sm_consent");
133
+ const newSearch = url.searchParams.toString() ? `?${url.searchParams.toString()}` : "";
134
+ window.history.replaceState(null, "", `${url.pathname}${newSearch}${url.hash}`);
135
+ return true;
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+ function queryNetworkConsentHub(hubUrl = DEFAULT_CONSENT_HUB_URL, timeoutMs = 1e3) {
141
+ if (typeof window === "undefined") return Promise.resolve(null);
142
+ try {
143
+ const hubOrigin = new URL(hubUrl).origin;
144
+ if (window.location.origin === hubOrigin) {
145
+ try {
146
+ const direct = window.localStorage?.getItem(NETWORK_CONSENT_STORAGE_KEY);
147
+ if (direct === "accepted" || direct === "rejected") {
148
+ return Promise.resolve(direct);
149
+ }
150
+ } catch {
151
+ }
152
+ return Promise.resolve(null);
153
+ }
154
+ return new Promise((resolve) => {
155
+ let settled = false;
156
+ let iframe = null;
157
+ let timer = null;
158
+ const finish = (result) => {
159
+ if (settled) return;
160
+ settled = true;
161
+ if (timer !== null) window.clearTimeout(timer);
162
+ window.removeEventListener("message", onMessage);
163
+ if (iframe && iframe.parentNode) {
164
+ try {
165
+ iframe.parentNode.removeChild(iframe);
166
+ } catch {
167
+ }
168
+ }
169
+ resolve(result);
170
+ };
171
+ const sendGet = () => {
172
+ try {
173
+ iframe?.contentWindow?.postMessage({ type: "SM_CONSENT_GET" }, hubOrigin);
174
+ } catch {
175
+ }
176
+ };
177
+ const onMessage = (event) => {
178
+ if (event.origin !== hubOrigin) return;
179
+ const data = event.data;
180
+ if (!data || typeof data !== "object") return;
181
+ if (data.type === "SM_CONSENT_STATUS") {
182
+ const status = data.status === "accepted" || data.status === "rejected" ? data.status : null;
183
+ finish(status);
184
+ } else if (data.type === "SM_CONSENT_BRIDGE_READY") {
185
+ sendGet();
186
+ }
187
+ };
188
+ window.addEventListener("message", onMessage);
189
+ timer = window.setTimeout(() => {
190
+ finish(null);
191
+ }, timeoutMs);
192
+ try {
193
+ iframe = document.createElement("iframe");
194
+ iframe.src = hubUrl;
195
+ iframe.style.position = "absolute";
196
+ iframe.style.width = "0";
197
+ iframe.style.height = "0";
198
+ iframe.style.border = "0";
199
+ iframe.style.display = "none";
200
+ iframe.style.visibility = "hidden";
201
+ iframe.setAttribute("aria-hidden", "true");
202
+ iframe.setAttribute("tabindex", "-1");
203
+ iframe.onload = () => {
204
+ sendGet();
205
+ };
206
+ iframe.onerror = () => {
207
+ finish(null);
208
+ };
209
+ const target = document.body || document.documentElement;
210
+ target.appendChild(iframe);
211
+ } catch {
212
+ finish(null);
213
+ }
214
+ });
215
+ } catch {
216
+ return Promise.resolve(null);
217
+ }
218
+ }
219
+ function setNetworkConsentHub(status, hubUrl = DEFAULT_CONSENT_HUB_URL) {
220
+ if (typeof window === "undefined") return;
221
+ try {
222
+ const hubOrigin = new URL(hubUrl).origin;
223
+ if (window.location.origin === hubOrigin) {
224
+ try {
225
+ if (status) {
226
+ window.localStorage?.setItem(NETWORK_CONSENT_STORAGE_KEY, status);
227
+ } else {
228
+ window.localStorage?.removeItem(NETWORK_CONSENT_STORAGE_KEY);
229
+ }
230
+ } catch {
231
+ }
232
+ return;
233
+ }
234
+ let iframe = null;
235
+ let timer = null;
236
+ const cleanup = () => {
237
+ if (timer !== null) window.clearTimeout(timer);
238
+ window.removeEventListener("message", onMessage);
239
+ if (iframe && iframe.parentNode) {
240
+ try {
241
+ iframe.parentNode.removeChild(iframe);
242
+ } catch {
243
+ }
244
+ }
245
+ };
246
+ const sendSet = () => {
247
+ try {
248
+ iframe?.contentWindow?.postMessage({ type: "SM_CONSENT_SET", status }, hubOrigin);
249
+ } catch {
250
+ }
251
+ };
252
+ const onMessage = (event) => {
253
+ if (event.origin !== hubOrigin) return;
254
+ const data = event.data;
255
+ if (!data || typeof data !== "object") return;
256
+ if (data.type === "SM_CONSENT_SAVED") {
257
+ cleanup();
258
+ } else if (data.type === "SM_CONSENT_BRIDGE_READY") {
259
+ sendSet();
260
+ }
261
+ };
262
+ window.addEventListener("message", onMessage);
263
+ timer = window.setTimeout(cleanup, 2500);
264
+ try {
265
+ iframe = document.createElement("iframe");
266
+ iframe.src = hubUrl;
267
+ iframe.style.position = "absolute";
268
+ iframe.style.width = "0";
269
+ iframe.style.height = "0";
270
+ iframe.style.border = "0";
271
+ iframe.style.display = "none";
272
+ iframe.style.visibility = "hidden";
273
+ iframe.setAttribute("aria-hidden", "true");
274
+ iframe.setAttribute("tabindex", "-1");
275
+ iframe.onload = () => {
276
+ sendSet();
277
+ };
278
+ iframe.onerror = cleanup;
279
+ const target = document.body || document.documentElement;
280
+ target.appendChild(iframe);
281
+ } catch {
282
+ cleanup();
283
+ }
284
+ } catch {
285
+ }
286
+ }
56
287
  function hasConsented() {
57
288
  return getCookieConsent() !== null;
58
289
  }
@@ -86,6 +317,8 @@ function setCookieConsent(preferences, source = "settings_save") {
86
317
  window.localStorage?.setItem(COOKIE_CONSENT_KEY, serialized);
87
318
  } catch {
88
319
  }
320
+ const networkStatus = mergedPreferences.performance || mergedPreferences.advertising ? "accepted" : "rejected";
321
+ setNetworkConsentHub(networkStatus);
89
322
  window.dispatchEvent(new CustomEvent(COOKIE_CONSENT_EVENT, { detail: record }));
90
323
  }
91
324
  return record;
@@ -129,16 +362,52 @@ function CookieConsentBanner({
129
362
  settingsUrl = "/cookies",
130
363
  className = "",
131
364
  publicationSlug,
132
- complianceEndpoint = "/api/compliance/consent"
365
+ complianceEndpoint = "/api/compliance/consent",
366
+ countryCode,
367
+ forceShow = false,
368
+ consentHubUrl = DEFAULT_CONSENT_HUB_URL
133
369
  }) {
134
370
  const [mounted, setMounted] = react.useState(false);
135
371
  const [visible, setVisible] = react.useState(false);
136
372
  react.useEffect(() => {
137
373
  setMounted(true);
138
- if (!hasConsented()) {
139
- const timer = window.setTimeout(() => setVisible(true), 300);
140
- return () => window.clearTimeout(timer);
374
+ if (checkUrlConsentBridge()) {
375
+ setVisible(false);
376
+ return;
377
+ }
378
+ if (hasConsented()) {
379
+ setVisible(false);
380
+ return;
381
+ }
382
+ if (!forceShow && !isGeoConsentRequired(countryCode)) {
383
+ setVisible(false);
384
+ void queryNetworkConsentHub(consentHubUrl, 1200).then((networkStatus) => {
385
+ if (networkStatus === "accepted") {
386
+ acceptAllCookies();
387
+ } else if (networkStatus === "rejected") {
388
+ rejectOptionalCookies();
389
+ }
390
+ });
391
+ return;
141
392
  }
393
+ let cancelled = false;
394
+ void queryNetworkConsentHub(consentHubUrl, 1e3).then((networkStatus) => {
395
+ if (cancelled) return;
396
+ if (networkStatus === "accepted") {
397
+ acceptAllCookies();
398
+ setVisible(false);
399
+ } else if (networkStatus === "rejected") {
400
+ rejectOptionalCookies();
401
+ setVisible(false);
402
+ } else {
403
+ setVisible(true);
404
+ }
405
+ });
406
+ return () => {
407
+ cancelled = true;
408
+ };
409
+ }, [countryCode, forceShow, consentHubUrl]);
410
+ react.useEffect(() => {
142
411
  const onConsentChange = () => {
143
412
  if (hasConsented()) {
144
413
  setVisible(false);
@@ -153,19 +422,21 @@ function CookieConsentBanner({
153
422
  setVisible(false);
154
423
  void recordConsentToCompliance(record, {
155
424
  endpoint: complianceEndpoint});
425
+ setNetworkConsentHub("accepted", consentHubUrl);
156
426
  };
157
427
  const handleReject = () => {
158
428
  const record = rejectOptionalCookies();
159
429
  setVisible(false);
160
430
  void recordConsentToCompliance(record, {
161
431
  endpoint: complianceEndpoint});
432
+ setNetworkConsentHub("rejected", consentHubUrl);
162
433
  };
163
434
  const defaultText = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
164
- "We use cookies and process data from your device to analyze website performance, personalize ad content, and improve your experience. Your consent includes data transfers outside of the country you\u2019re located. View",
435
+ "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
165
436
  " ",
166
437
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
167
438
  " ",
168
- "for more information."
439
+ "for details."
169
440
  ] });
170
441
  return /* @__PURE__ */ jsxRuntime.jsx(
171
442
  "aside",
@@ -13,7 +13,13 @@ interface CookieConsentBannerProps {
13
13
  publicationSlug?: string;
14
14
  /** Compliance recording endpoint. Default: "/api/compliance/consent" */
15
15
  complianceEndpoint?: string;
16
+ /** Explicit ISO 3166-1 alpha-2 country code (e.g. from server headers or GeoIP) */
17
+ countryCode?: string | null;
18
+ /** Force display of banner regardless of visitor geography */
19
+ forceShow?: boolean;
20
+ /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
+ consentHubUrl?: string;
16
22
  }
17
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, }: CookieConsentBannerProps): react__default.JSX.Element | null;
23
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): react__default.JSX.Element | null;
18
24
 
19
25
  export { CookieConsentBanner, type CookieConsentBannerProps };
@@ -13,7 +13,13 @@ interface CookieConsentBannerProps {
13
13
  publicationSlug?: string;
14
14
  /** Compliance recording endpoint. Default: "/api/compliance/consent" */
15
15
  complianceEndpoint?: string;
16
+ /** Explicit ISO 3166-1 alpha-2 country code (e.g. from server headers or GeoIP) */
17
+ countryCode?: string | null;
18
+ /** Force display of banner regardless of visitor geography */
19
+ forceShow?: boolean;
20
+ /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
+ consentHubUrl?: string;
16
22
  }
17
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, }: CookieConsentBannerProps): react__default.JSX.Element | null;
23
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): react__default.JSX.Element | null;
18
24
 
19
25
  export { CookieConsentBanner, type CookieConsentBannerProps };
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieConsentBanner } from './chunk-5QHNNRVQ.js';
3
- import './chunk-RNXTBN5Z.js';
2
+ export { CookieConsentBanner } from './chunk-5MHPE77S.js';
3
+ import './chunk-L5OE6DND.js';
@@ -9,6 +9,8 @@ var COOKIE_CONSENT_KEY = "sm_cookie_consent_v1";
9
9
  var COOKIE_CONSENT_BACKUP_KEY = "napsite_cookie_consent_v1";
10
10
  var COOKIE_CONSENT_EVENT = "scalemule-cookie-consent-change";
11
11
  var COOKIE_CONSENT_MAX_AGE_SECONDS = 365 * 24 * 60 * 60;
12
+ var DEFAULT_CONSENT_HUB_URL = "https://bayareachronicle.com/consent-bridge.html";
13
+ var NETWORK_CONSENT_STORAGE_KEY = "sm_network_consent_status";
12
14
  var DEFAULT_PREFERENCES_REJECTED = {
13
15
  essential: true,
14
16
  performance: false,
@@ -163,6 +165,72 @@ function getCookieConsent() {
163
165
  }
164
166
  return memoryConsent;
165
167
  }
168
+ function setNetworkConsentHub(status, hubUrl = DEFAULT_CONSENT_HUB_URL) {
169
+ if (typeof window === "undefined") return;
170
+ try {
171
+ const hubOrigin = new URL(hubUrl).origin;
172
+ if (window.location.origin === hubOrigin) {
173
+ try {
174
+ if (status) {
175
+ window.localStorage?.setItem(NETWORK_CONSENT_STORAGE_KEY, status);
176
+ }
177
+ } catch {
178
+ }
179
+ return;
180
+ }
181
+ let iframe = null;
182
+ let timer = null;
183
+ const cleanup = () => {
184
+ if (timer !== null) window.clearTimeout(timer);
185
+ window.removeEventListener("message", onMessage);
186
+ if (iframe && iframe.parentNode) {
187
+ try {
188
+ iframe.parentNode.removeChild(iframe);
189
+ } catch {
190
+ }
191
+ }
192
+ };
193
+ const sendSet = () => {
194
+ try {
195
+ iframe?.contentWindow?.postMessage({ type: "SM_CONSENT_SET", status }, hubOrigin);
196
+ } catch {
197
+ }
198
+ };
199
+ const onMessage = (event) => {
200
+ if (event.origin !== hubOrigin) return;
201
+ const data = event.data;
202
+ if (!data || typeof data !== "object") return;
203
+ if (data.type === "SM_CONSENT_SAVED") {
204
+ cleanup();
205
+ } else if (data.type === "SM_CONSENT_BRIDGE_READY") {
206
+ sendSet();
207
+ }
208
+ };
209
+ window.addEventListener("message", onMessage);
210
+ timer = window.setTimeout(cleanup, 2500);
211
+ try {
212
+ iframe = document.createElement("iframe");
213
+ iframe.src = hubUrl;
214
+ iframe.style.position = "absolute";
215
+ iframe.style.width = "0";
216
+ iframe.style.height = "0";
217
+ iframe.style.border = "0";
218
+ iframe.style.display = "none";
219
+ iframe.style.visibility = "hidden";
220
+ iframe.setAttribute("aria-hidden", "true");
221
+ iframe.setAttribute("tabindex", "-1");
222
+ iframe.onload = () => {
223
+ sendSet();
224
+ };
225
+ iframe.onerror = cleanup;
226
+ const target = document.body || document.documentElement;
227
+ target.appendChild(iframe);
228
+ } catch {
229
+ cleanup();
230
+ }
231
+ } catch {
232
+ }
233
+ }
166
234
  function setCookieConsent(preferences, source = "settings_save") {
167
235
  const mergedPreferences = {
168
236
  essential: true,
@@ -193,6 +261,8 @@ function setCookieConsent(preferences, source = "settings_save") {
193
261
  window.localStorage?.setItem(COOKIE_CONSENT_KEY, serialized);
194
262
  } catch {
195
263
  }
264
+ const networkStatus = mergedPreferences.performance || mergedPreferences.advertising ? "accepted" : "rejected";
265
+ setNetworkConsentHub(networkStatus);
196
266
  window.dispatchEvent(new CustomEvent(COOKIE_CONSENT_EVENT, { detail: record }));
197
267
  }
198
268
  return record;
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieSettingsView } from './chunk-AYSBXLOT.js';
3
- import './chunk-RNXTBN5Z.js';
2
+ export { CookieSettingsView } from './chunk-FUUL2PSF.js';
3
+ import './chunk-L5OE6DND.js';