@scalemule/news-publication 0.0.11 → 0.0.12

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.
@@ -157,6 +157,101 @@ function getCookieConsent() {
157
157
  }
158
158
  return memoryConsent;
159
159
  }
160
+ var EU_EEA_UK_COUNTRIES = /* @__PURE__ */ new Set([
161
+ "AT",
162
+ "BE",
163
+ "BG",
164
+ "HR",
165
+ "CY",
166
+ "CZ",
167
+ "DK",
168
+ "EE",
169
+ "FI",
170
+ "FR",
171
+ "DE",
172
+ "GR",
173
+ "HU",
174
+ "IE",
175
+ "IT",
176
+ "LV",
177
+ "LT",
178
+ "LU",
179
+ "MT",
180
+ "NL",
181
+ "PL",
182
+ "PT",
183
+ "RO",
184
+ "SK",
185
+ "SI",
186
+ "ES",
187
+ "SE",
188
+ "IS",
189
+ "LI",
190
+ "NO",
191
+ "GB",
192
+ "CH"
193
+ ]);
194
+ function isGeoConsentRequired(countryCode) {
195
+ if (countryCode && typeof countryCode === "string") {
196
+ const code = countryCode.trim().toUpperCase();
197
+ if (code.length === 2) {
198
+ return EU_EEA_UK_COUNTRIES.has(code);
199
+ }
200
+ }
201
+ if (typeof window !== "undefined") {
202
+ try {
203
+ const cached = readCookie("sm_geo_country") || window.localStorage?.getItem("sm_geo_country");
204
+ if (cached && typeof cached === "string" && cached.length === 2) {
205
+ return EU_EEA_UK_COUNTRIES.has(cached.toUpperCase());
206
+ }
207
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
208
+ if (tz) {
209
+ if (/^(America|US|Canada|Pacific)\//i.test(tz)) {
210
+ return false;
211
+ }
212
+ if (/^(Europe|Atlantic\/Reykjavik|Atlantic\/Canary|Atlantic\/Faeroe|Atlantic\/Madeira|Atlantic\/Azores|WET|CET|EET|GMT)/i.test(tz)) {
213
+ return true;
214
+ }
215
+ }
216
+ } catch {
217
+ }
218
+ }
219
+ return false;
220
+ }
221
+ function checkUrlConsentBridge() {
222
+ if (typeof window === "undefined") return false;
223
+ try {
224
+ const url = new URL(window.location.href);
225
+ const param = url.searchParams.get("sm_consent");
226
+ if (!param) return false;
227
+ if (param === "accepted" || param === "1" || param === "all") {
228
+ acceptAllCookies();
229
+ } else if (param === "rejected" || param === "0" || param === "essential") {
230
+ rejectOptionalCookies();
231
+ } else {
232
+ return false;
233
+ }
234
+ url.searchParams.delete("sm_consent");
235
+ const newSearch = url.searchParams.toString() ? `?${url.searchParams.toString()}` : "";
236
+ window.history.replaceState(null, "", `${url.pathname}${newSearch}${url.hash}`);
237
+ return true;
238
+ } catch {
239
+ return false;
240
+ }
241
+ }
242
+ function decorateNetworkUrl(targetUrl) {
243
+ const consent = getCookieConsent();
244
+ if (!consent) return targetUrl;
245
+ try {
246
+ const base = typeof window !== "undefined" ? window.location.origin : "https://localhost";
247
+ const url = new URL(targetUrl, base);
248
+ const value = consent.preferences.performance || consent.preferences.advertising ? "1" : "0";
249
+ url.searchParams.set("sm_consent", value);
250
+ return url.toString();
251
+ } catch {
252
+ return targetUrl;
253
+ }
254
+ }
160
255
  function hasConsented() {
161
256
  return getCookieConsent() !== null;
162
257
  }
@@ -256,4 +351,4 @@ async function recordConsentToCompliance(record, options) {
256
351
  }
257
352
  }
258
353
 
259
- export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, acceptAllCookies, clearCookieConsent, getCookieConsent, hasConsented, isCategoryAllowed, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent };
354
+ export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { hasConsented, COOKIE_CONSENT_EVENT, acceptAllCookies, recordConsentToCompliance, rejectOptionalCookies } from './chunk-RNXTBN5Z.js';
2
+ import { checkUrlConsentBridge, hasConsented, isGeoConsentRequired, COOKIE_CONSENT_EVENT, acceptAllCookies, recordConsentToCompliance, rejectOptionalCookies } from './chunk-CWZWU4HE.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -9,16 +9,30 @@ function CookieConsentBanner({
9
9
  settingsUrl = "/cookies",
10
10
  className = "",
11
11
  publicationSlug,
12
- complianceEndpoint = "/api/compliance/consent"
12
+ complianceEndpoint = "/api/compliance/consent",
13
+ countryCode,
14
+ forceShow = false
13
15
  }) {
14
16
  const [mounted, setMounted] = useState(false);
15
17
  const [visible, setVisible] = useState(false);
16
18
  useEffect(() => {
17
19
  setMounted(true);
18
- if (!hasConsented()) {
19
- const timer = window.setTimeout(() => setVisible(true), 300);
20
- return () => window.clearTimeout(timer);
20
+ if (checkUrlConsentBridge()) {
21
+ setVisible(false);
22
+ return;
21
23
  }
24
+ if (hasConsented()) {
25
+ setVisible(false);
26
+ return;
27
+ }
28
+ if (!forceShow && !isGeoConsentRequired(countryCode)) {
29
+ setVisible(false);
30
+ return;
31
+ }
32
+ const timer = window.setTimeout(() => setVisible(true), 300);
33
+ return () => window.clearTimeout(timer);
34
+ }, [countryCode, forceShow]);
35
+ useEffect(() => {
22
36
  const onConsentChange = () => {
23
37
  if (hasConsented()) {
24
38
  setVisible(false);
@@ -45,11 +59,11 @@ function CookieConsentBanner({
45
59
  });
46
60
  };
47
61
  const defaultText = /* @__PURE__ */ jsxs(Fragment, { children: [
48
- "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",
62
+ "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
49
63
  " ",
50
64
  /* @__PURE__ */ jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
51
65
  " ",
52
- "for more information."
66
+ "for details."
53
67
  ] });
54
68
  return /* @__PURE__ */ jsx(
55
69
  "aside",
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-RNXTBN5Z.js';
2
+ import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-CWZWU4HE.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -53,6 +53,88 @@ function getCookieConsent() {
53
53
  }
54
54
  return memoryConsent;
55
55
  }
56
+ var EU_EEA_UK_COUNTRIES = /* @__PURE__ */ new Set([
57
+ "AT",
58
+ "BE",
59
+ "BG",
60
+ "HR",
61
+ "CY",
62
+ "CZ",
63
+ "DK",
64
+ "EE",
65
+ "FI",
66
+ "FR",
67
+ "DE",
68
+ "GR",
69
+ "HU",
70
+ "IE",
71
+ "IT",
72
+ "LV",
73
+ "LT",
74
+ "LU",
75
+ "MT",
76
+ "NL",
77
+ "PL",
78
+ "PT",
79
+ "RO",
80
+ "SK",
81
+ "SI",
82
+ "ES",
83
+ "SE",
84
+ "IS",
85
+ "LI",
86
+ "NO",
87
+ "GB",
88
+ "CH"
89
+ ]);
90
+ function isGeoConsentRequired(countryCode) {
91
+ if (countryCode && typeof countryCode === "string") {
92
+ const code = countryCode.trim().toUpperCase();
93
+ if (code.length === 2) {
94
+ return EU_EEA_UK_COUNTRIES.has(code);
95
+ }
96
+ }
97
+ if (typeof window !== "undefined") {
98
+ try {
99
+ const cached = readCookie("sm_geo_country") || window.localStorage?.getItem("sm_geo_country");
100
+ if (cached && typeof cached === "string" && cached.length === 2) {
101
+ return EU_EEA_UK_COUNTRIES.has(cached.toUpperCase());
102
+ }
103
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
104
+ if (tz) {
105
+ if (/^(America|US|Canada|Pacific)\//i.test(tz)) {
106
+ return false;
107
+ }
108
+ if (/^(Europe|Atlantic\/Reykjavik|Atlantic\/Canary|Atlantic\/Faeroe|Atlantic\/Madeira|Atlantic\/Azores|WET|CET|EET|GMT)/i.test(tz)) {
109
+ return true;
110
+ }
111
+ }
112
+ } catch {
113
+ }
114
+ }
115
+ return false;
116
+ }
117
+ function checkUrlConsentBridge() {
118
+ if (typeof window === "undefined") return false;
119
+ try {
120
+ const url = new URL(window.location.href);
121
+ const param = url.searchParams.get("sm_consent");
122
+ if (!param) return false;
123
+ if (param === "accepted" || param === "1" || param === "all") {
124
+ acceptAllCookies();
125
+ } else if (param === "rejected" || param === "0" || param === "essential") {
126
+ rejectOptionalCookies();
127
+ } else {
128
+ return false;
129
+ }
130
+ url.searchParams.delete("sm_consent");
131
+ const newSearch = url.searchParams.toString() ? `?${url.searchParams.toString()}` : "";
132
+ window.history.replaceState(null, "", `${url.pathname}${newSearch}${url.hash}`);
133
+ return true;
134
+ } catch {
135
+ return false;
136
+ }
137
+ }
56
138
  function hasConsented() {
57
139
  return getCookieConsent() !== null;
58
140
  }
@@ -129,16 +211,30 @@ function CookieConsentBanner({
129
211
  settingsUrl = "/cookies",
130
212
  className = "",
131
213
  publicationSlug,
132
- complianceEndpoint = "/api/compliance/consent"
214
+ complianceEndpoint = "/api/compliance/consent",
215
+ countryCode,
216
+ forceShow = false
133
217
  }) {
134
218
  const [mounted, setMounted] = react.useState(false);
135
219
  const [visible, setVisible] = react.useState(false);
136
220
  react.useEffect(() => {
137
221
  setMounted(true);
138
- if (!hasConsented()) {
139
- const timer = window.setTimeout(() => setVisible(true), 300);
140
- return () => window.clearTimeout(timer);
222
+ if (checkUrlConsentBridge()) {
223
+ setVisible(false);
224
+ return;
225
+ }
226
+ if (hasConsented()) {
227
+ setVisible(false);
228
+ return;
141
229
  }
230
+ if (!forceShow && !isGeoConsentRequired(countryCode)) {
231
+ setVisible(false);
232
+ return;
233
+ }
234
+ const timer = window.setTimeout(() => setVisible(true), 300);
235
+ return () => window.clearTimeout(timer);
236
+ }, [countryCode, forceShow]);
237
+ react.useEffect(() => {
142
238
  const onConsentChange = () => {
143
239
  if (hasConsented()) {
144
240
  setVisible(false);
@@ -161,11 +257,11 @@ function CookieConsentBanner({
161
257
  endpoint: complianceEndpoint});
162
258
  };
163
259
  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",
260
+ "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
165
261
  " ",
166
262
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
167
263
  " ",
168
- "for more information."
264
+ "for details."
169
265
  ] });
170
266
  return /* @__PURE__ */ jsxRuntime.jsx(
171
267
  "aside",
@@ -13,7 +13,11 @@ 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;
16
20
  }
17
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, }: CookieConsentBannerProps): react__default.JSX.Element | null;
21
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, }: CookieConsentBannerProps): react__default.JSX.Element | null;
18
22
 
19
23
  export { CookieConsentBanner, type CookieConsentBannerProps };
@@ -13,7 +13,11 @@ 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;
16
20
  }
17
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, }: CookieConsentBannerProps): react__default.JSX.Element | null;
21
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, }: CookieConsentBannerProps): react__default.JSX.Element | null;
18
22
 
19
23
  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-EKQXS63J.js';
3
+ import './chunk-CWZWU4HE.js';
@@ -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-ZSA6YQBV.js';
3
+ import './chunk-CWZWU4HE.js';
package/dist/index.cjs CHANGED
@@ -942,6 +942,101 @@ function getCookieConsent() {
942
942
  }
943
943
  return memoryConsent;
944
944
  }
945
+ var EU_EEA_UK_COUNTRIES = /* @__PURE__ */ new Set([
946
+ "AT",
947
+ "BE",
948
+ "BG",
949
+ "HR",
950
+ "CY",
951
+ "CZ",
952
+ "DK",
953
+ "EE",
954
+ "FI",
955
+ "FR",
956
+ "DE",
957
+ "GR",
958
+ "HU",
959
+ "IE",
960
+ "IT",
961
+ "LV",
962
+ "LT",
963
+ "LU",
964
+ "MT",
965
+ "NL",
966
+ "PL",
967
+ "PT",
968
+ "RO",
969
+ "SK",
970
+ "SI",
971
+ "ES",
972
+ "SE",
973
+ "IS",
974
+ "LI",
975
+ "NO",
976
+ "GB",
977
+ "CH"
978
+ ]);
979
+ function isGeoConsentRequired(countryCode) {
980
+ if (countryCode && typeof countryCode === "string") {
981
+ const code = countryCode.trim().toUpperCase();
982
+ if (code.length === 2) {
983
+ return EU_EEA_UK_COUNTRIES.has(code);
984
+ }
985
+ }
986
+ if (typeof window !== "undefined") {
987
+ try {
988
+ const cached = readCookie("sm_geo_country") || window.localStorage?.getItem("sm_geo_country");
989
+ if (cached && typeof cached === "string" && cached.length === 2) {
990
+ return EU_EEA_UK_COUNTRIES.has(cached.toUpperCase());
991
+ }
992
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
993
+ if (tz) {
994
+ if (/^(America|US|Canada|Pacific)\//i.test(tz)) {
995
+ return false;
996
+ }
997
+ if (/^(Europe|Atlantic\/Reykjavik|Atlantic\/Canary|Atlantic\/Faeroe|Atlantic\/Madeira|Atlantic\/Azores|WET|CET|EET|GMT)/i.test(tz)) {
998
+ return true;
999
+ }
1000
+ }
1001
+ } catch {
1002
+ }
1003
+ }
1004
+ return false;
1005
+ }
1006
+ function checkUrlConsentBridge() {
1007
+ if (typeof window === "undefined") return false;
1008
+ try {
1009
+ const url = new URL(window.location.href);
1010
+ const param = url.searchParams.get("sm_consent");
1011
+ if (!param) return false;
1012
+ if (param === "accepted" || param === "1" || param === "all") {
1013
+ acceptAllCookies();
1014
+ } else if (param === "rejected" || param === "0" || param === "essential") {
1015
+ rejectOptionalCookies();
1016
+ } else {
1017
+ return false;
1018
+ }
1019
+ url.searchParams.delete("sm_consent");
1020
+ const newSearch = url.searchParams.toString() ? `?${url.searchParams.toString()}` : "";
1021
+ window.history.replaceState(null, "", `${url.pathname}${newSearch}${url.hash}`);
1022
+ return true;
1023
+ } catch {
1024
+ return false;
1025
+ }
1026
+ }
1027
+ function decorateNetworkUrl(targetUrl) {
1028
+ const consent = getCookieConsent();
1029
+ if (!consent) return targetUrl;
1030
+ try {
1031
+ const base = typeof window !== "undefined" ? window.location.origin : "https://localhost";
1032
+ const url = new URL(targetUrl, base);
1033
+ const value = consent.preferences.performance || consent.preferences.advertising ? "1" : "0";
1034
+ url.searchParams.set("sm_consent", value);
1035
+ return url.toString();
1036
+ } catch {
1037
+ return targetUrl;
1038
+ }
1039
+ }
945
1040
  function hasConsented() {
946
1041
  return getCookieConsent() !== null;
947
1042
  }
@@ -1046,16 +1141,30 @@ function CookieConsentBanner({
1046
1141
  settingsUrl = "/cookies",
1047
1142
  className = "",
1048
1143
  publicationSlug,
1049
- complianceEndpoint = "/api/compliance/consent"
1144
+ complianceEndpoint = "/api/compliance/consent",
1145
+ countryCode,
1146
+ forceShow = false
1050
1147
  }) {
1051
1148
  const [mounted, setMounted] = react.useState(false);
1052
1149
  const [visible, setVisible] = react.useState(false);
1053
1150
  react.useEffect(() => {
1054
1151
  setMounted(true);
1055
- if (!hasConsented()) {
1056
- const timer = window.setTimeout(() => setVisible(true), 300);
1057
- return () => window.clearTimeout(timer);
1152
+ if (checkUrlConsentBridge()) {
1153
+ setVisible(false);
1154
+ return;
1058
1155
  }
1156
+ if (hasConsented()) {
1157
+ setVisible(false);
1158
+ return;
1159
+ }
1160
+ if (!forceShow && !isGeoConsentRequired(countryCode)) {
1161
+ setVisible(false);
1162
+ return;
1163
+ }
1164
+ const timer = window.setTimeout(() => setVisible(true), 300);
1165
+ return () => window.clearTimeout(timer);
1166
+ }, [countryCode, forceShow]);
1167
+ react.useEffect(() => {
1059
1168
  const onConsentChange = () => {
1060
1169
  if (hasConsented()) {
1061
1170
  setVisible(false);
@@ -1078,11 +1187,11 @@ function CookieConsentBanner({
1078
1187
  endpoint: complianceEndpoint});
1079
1188
  };
1080
1189
  const defaultText = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1081
- "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",
1190
+ "We use cookies and device data to analyze performance, support local sponsors, and remember your reading preferences across our news network. View",
1082
1191
  " ",
1083
1192
  /* @__PURE__ */ jsxRuntime.jsx("a", { href: settingsUrl, className: "sm-cookie-banner-link", children: "Cookie Settings" }),
1084
1193
  " ",
1085
- "for more information."
1194
+ "for details."
1086
1195
  ] });
1087
1196
  return /* @__PURE__ */ jsxRuntime.jsx(
1088
1197
  "aside",
@@ -1662,6 +1771,7 @@ exports.CookieConsentBanner = CookieConsentBanner;
1662
1771
  exports.CookieSettingsView = CookieSettingsView;
1663
1772
  exports.DEFAULT_PREFERENCES_ACCEPTED = DEFAULT_PREFERENCES_ACCEPTED;
1664
1773
  exports.DEFAULT_PREFERENCES_REJECTED = DEFAULT_PREFERENCES_REJECTED;
1774
+ exports.EU_EEA_UK_COUNTRIES = EU_EEA_UK_COUNTRIES;
1665
1775
  exports.EVENTS = EVENTS;
1666
1776
  exports.FEATURE_NAMES = FEATURE_NAMES;
1667
1777
  exports.JOB_EMPLOYMENT_TYPES = JOB_EMPLOYMENT_TYPES;
@@ -1691,6 +1801,7 @@ exports.attribution = attribution;
1691
1801
  exports.briefSummary = briefSummary;
1692
1802
  exports.canHostMeetup = canHostMeetup;
1693
1803
  exports.categoryLabel = categoryLabel;
1804
+ exports.checkUrlConsentBridge = checkUrlConsentBridge;
1694
1805
  exports.classifiedPath = classifiedPath;
1695
1806
  exports.classifiedSearchPath = classifiedSearchPath;
1696
1807
  exports.clearCookieConsent = clearCookieConsent;
@@ -1700,6 +1811,7 @@ exports.composeEdition = composeEdition;
1700
1811
  exports.dealDollars = dealDollars;
1701
1812
  exports.dealGoPath = dealGoPath;
1702
1813
  exports.dealsFeedPath = dealsFeedPath;
1814
+ exports.decorateNetworkUrl = decorateNetworkUrl;
1703
1815
  exports.defaultRevalidateTargets = defaultRevalidateTargets;
1704
1816
  exports.definePublication = definePublication;
1705
1817
  exports.displayHeadline = displayHeadline;
@@ -1719,6 +1831,7 @@ exports.isAdult = isAdult;
1719
1831
  exports.isCategoryAllowed = isCategoryAllowed;
1720
1832
  exports.isClassifiedCategory = isClassifiedCategory;
1721
1833
  exports.isEmploymentType = isEmploymentType;
1834
+ exports.isGeoConsentRequired = isGeoConsentRequired;
1722
1835
  exports.jobEmployerBody = jobEmployerBody;
1723
1836
  exports.jobOpeningBody = jobOpeningBody;
1724
1837
  exports.jobPostingBody = jobPostingBody;
package/dist/index.d.cts CHANGED
@@ -280,6 +280,28 @@ declare const COOKIE_CATEGORIES: CookieCategoryDefinition[];
280
280
  * Checks HTTP cookie first, then localStorage fallback, then in-memory state.
281
281
  */
282
282
  declare function getCookieConsent(): CookieConsentRecord | null;
283
+ /**
284
+ * ISO 3166-1 alpha-2 country codes for EU/EEA member states and the UK/Switzerland
285
+ * subject to the GDPR and ePrivacy Directive prior-consent requirement.
286
+ */
287
+ declare const EU_EEA_UK_COUNTRIES: Set<string>;
288
+ /**
289
+ * Determine if cookie consent prior-opt-in banner is required based on visitor geography.
290
+ * For US readers (including California CCPA/CPRA), opt-in banners are not required.
291
+ * Returns true ONLY if the visitor is identified as being within an EU/EEA/UK jurisdiction.
292
+ */
293
+ declare function isGeoConsentRequired(countryCode?: string | null): boolean;
294
+ /**
295
+ * Checks for a cross-domain network consent signal in the URL query (?sm_consent=accepted|rejected|1|0).
296
+ * If present, silently records the consent choice locally and removes the param from the address bar.
297
+ * Returns true if a consent signal was processed.
298
+ */
299
+ declare function checkUrlConsentBridge(): boolean;
300
+ /**
301
+ * Appends the current consent decision to an outgoing URL within the newspaper network
302
+ * so sister publications can adopt the user's choice without requiring a new banner prompt.
303
+ */
304
+ declare function decorateNetworkUrl(targetUrl: string): string;
283
305
  /**
284
306
  * Check if the user has already provided an explicit consent decision.
285
307
  */
@@ -356,4 +378,4 @@ declare function subscribeTheme(listener: () => void): () => void;
356
378
  */
357
379
  declare function themeInitScript(): string;
358
380
 
359
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
381
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, checkUrlConsentBridge, clearCookieConsent, clearTheme, composeEdition, decorateNetworkUrl, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, isGeoConsentRequired, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.d.ts CHANGED
@@ -280,6 +280,28 @@ declare const COOKIE_CATEGORIES: CookieCategoryDefinition[];
280
280
  * Checks HTTP cookie first, then localStorage fallback, then in-memory state.
281
281
  */
282
282
  declare function getCookieConsent(): CookieConsentRecord | null;
283
+ /**
284
+ * ISO 3166-1 alpha-2 country codes for EU/EEA member states and the UK/Switzerland
285
+ * subject to the GDPR and ePrivacy Directive prior-consent requirement.
286
+ */
287
+ declare const EU_EEA_UK_COUNTRIES: Set<string>;
288
+ /**
289
+ * Determine if cookie consent prior-opt-in banner is required based on visitor geography.
290
+ * For US readers (including California CCPA/CPRA), opt-in banners are not required.
291
+ * Returns true ONLY if the visitor is identified as being within an EU/EEA/UK jurisdiction.
292
+ */
293
+ declare function isGeoConsentRequired(countryCode?: string | null): boolean;
294
+ /**
295
+ * Checks for a cross-domain network consent signal in the URL query (?sm_consent=accepted|rejected|1|0).
296
+ * If present, silently records the consent choice locally and removes the param from the address bar.
297
+ * Returns true if a consent signal was processed.
298
+ */
299
+ declare function checkUrlConsentBridge(): boolean;
300
+ /**
301
+ * Appends the current consent decision to an outgoing URL within the newspaper network
302
+ * so sister publications can adopt the user's choice without requiring a new banner prompt.
303
+ */
304
+ declare function decorateNetworkUrl(targetUrl: string): string;
283
305
  /**
284
306
  * Check if the user has already provided an explicit consent decision.
285
307
  */
@@ -356,4 +378,4 @@ declare function subscribeTheme(listener: () => void): () => void;
356
378
  */
357
379
  declare function themeInitScript(): string;
358
380
 
359
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearCookieConsent, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
381
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, type CookieCategory, type CookieCategoryDefinition, type CookieConsentRecord, type CookieDisclosure, type CookiePreferences, CouponOffer, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, acceptAllCookies, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, checkUrlConsentBridge, clearCookieConsent, clearTheme, composeEdition, decorateNetworkUrl, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getCookieConsent, getStoredTheme, getSystemTheme, hasConsented, inventoryCacheOptions, isCategoryAllowed, isGeoConsentRequired, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, rejectOptionalCookies, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setCookieConsent, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ import { definePublication } from './chunk-C7JTEHTH.js';
3
3
  export { BROADSHEET_THEME, FEATURE_NAMES, LAYOUT_COOKIE, acceptRemoteRecord, acceptSnapshot, definePublication, featureEnabled, parseSnapshot, publicationFromSnapshot, publicationRecord, resolveLayout, snapshotFromRecord, syncPublication } from './chunk-C7JTEHTH.js';
4
4
  export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedPath, classifiedSearchPath, clipChars, dealDollars, dealGoPath, dealsFeedPath, employmentLabel, isClassifiedCategory, isEmploymentType, jobEmployerBody, jobOpeningBody, jobPostingBody, prepareClassified, prepareJobPost, publicJobPath, publicJobsPath } from './chunk-C7ZPLH2K.js';
5
5
  export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup, screenMessage } from './chunk-GB36YM56.js';
6
- export { CookieConsentBanner } from './chunk-5QHNNRVQ.js';
7
- export { CookieSettingsView } from './chunk-AYSBXLOT.js';
8
- export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, acceptAllCookies, clearCookieConsent, getCookieConsent, hasConsented, isCategoryAllowed, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent } from './chunk-RNXTBN5Z.js';
6
+ export { CookieConsentBanner } from './chunk-EKQXS63J.js';
7
+ export { CookieSettingsView } from './chunk-ZSA6YQBV.js';
8
+ export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent } from './chunk-CWZWU4HE.js';
9
9
 
10
10
  // src/preset.ts
11
11
  var walnutCreekTimes = definePublication({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/news-publication",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "description": "Shared server framework for ScaleMule local news publications. Walnut Creek Times is the broadsheet default; each paper supplies its own identity and feature flags.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",