@scalemule/news-publication 0.0.16 → 0.0.17

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.
@@ -1,10 +1,38 @@
1
- import { recordConsentToCompliance as recordConsentToCompliance$1 } from '@scalemule/compliance';
1
+ import { recordConsentToCompliance as recordConsentToCompliance$1, getCookieConsent, decorateNetworkUrl } from '@scalemule/compliance';
2
2
  export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, queryNetworkConsentHub, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from '@scalemule/compliance';
3
3
 
4
4
  // src/cookies.ts
5
5
  async function recordConsentToCompliance(record, options) {
6
6
  return recordConsentToCompliance$1(record, options);
7
7
  }
8
+ var delegationInstalled = false;
9
+ function setupNetworkConsentDelegation() {
10
+ if (typeof window === "undefined" || delegationInstalled) return;
11
+ delegationInstalled = true;
12
+ document.addEventListener(
13
+ "click",
14
+ (e) => {
15
+ const target = e.target;
16
+ const anchor = target?.closest("a");
17
+ if (!anchor || !anchor.href) return;
18
+ try {
19
+ const url = new URL(anchor.href, window.location.href);
20
+ if (url.protocol !== "http:" && url.protocol !== "https:") return;
21
+ if (isNetworkHostname(url.hostname)) {
22
+ const consent = getCookieConsent();
23
+ if (consent) {
24
+ const decorated = decorateNetworkUrl(anchor.href);
25
+ if (decorated !== anchor.href) {
26
+ anchor.href = decorated;
27
+ }
28
+ }
29
+ }
30
+ } catch {
31
+ }
32
+ },
33
+ { capture: true }
34
+ );
35
+ }
8
36
  var COOKIE_CATEGORIES = [
9
37
  {
10
38
  id: "essential",
@@ -114,4 +142,121 @@ var COOKIE_CATEGORIES = [
114
142
  }
115
143
  ];
116
144
 
117
- export { COOKIE_CATEGORIES, recordConsentToCompliance };
145
+ // src/network.ts
146
+ var localPublications = [
147
+ {
148
+ slug: "bayareachronicle",
149
+ name: "Bay Area Chronicle",
150
+ url: "https://bayareachronicle.com",
151
+ area: "San Francisco Bay Area",
152
+ description: "The parent home for our local community publications."
153
+ },
154
+ {
155
+ slug: "concordchronicle",
156
+ name: "Concord Chronicle",
157
+ url: "https://concordchronicle.com",
158
+ area: "Concord",
159
+ description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
160
+ },
161
+ {
162
+ slug: "walnutcreektimes",
163
+ name: "Walnut Creek Times",
164
+ url: "https://walnutcreektimes.com",
165
+ area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
166
+ description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
167
+ },
168
+ {
169
+ slug: "lamorindapost",
170
+ name: "Lamorinda Post",
171
+ url: "https://lamorindapost.com",
172
+ area: "Orinda \xB7 Lafayette \xB7 Moraga",
173
+ description: "A new home for the neighbors, businesses and community stories of Lamorinda."
174
+ },
175
+ {
176
+ slug: "alamoweekly",
177
+ name: "Alamo Weekly",
178
+ url: "https://alamoweekly.com",
179
+ area: "Alamo",
180
+ description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
181
+ },
182
+ {
183
+ slug: "danvilletimes",
184
+ name: "Danville Times",
185
+ url: "https://danvilletimes.com",
186
+ area: "Danville",
187
+ description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
188
+ },
189
+ {
190
+ slug: "sanramontimes",
191
+ name: "San Ramon Times",
192
+ url: "https://sanramontimes.com",
193
+ area: "San Ramon",
194
+ description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
195
+ },
196
+ {
197
+ slug: "diablovalleynews",
198
+ name: "Diablo Valley News",
199
+ url: "https://diablovalleynews.com",
200
+ area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
201
+ description: "Local stories about the people, businesses and community life of the Diablo Valley."
202
+ },
203
+ {
204
+ slug: "trivalleyweekly",
205
+ name: "Tri-Valley Weekly",
206
+ url: "https://trivalleyweekly.com",
207
+ area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
208
+ description: "Local stories about the businesses, people and community life of the Tri-Valley."
209
+ },
210
+ {
211
+ slug: "eastbaypost",
212
+ name: "East Bay Post",
213
+ url: "https://eastbaypost.com",
214
+ area: "The East Bay",
215
+ description: "A new publication connecting the people and neighborhoods of the East Bay."
216
+ },
217
+ {
218
+ slug: "napagate",
219
+ name: "Napa Gate",
220
+ url: "https://napagate.com",
221
+ area: "Napa Valley",
222
+ description: "A new local publication for the people, places and everyday life of Napa Valley.",
223
+ listed: false
224
+ }
225
+ ];
226
+ var listedPublications = localPublications.filter(
227
+ (publication) => !("listed" in publication) || publication.listed
228
+ );
229
+ var communityPublications = listedPublications.filter(
230
+ (publication) => publication.slug !== "bayareachronicle"
231
+ );
232
+ function findPublicationBySlug(slug) {
233
+ return localPublications.find((p) => p.slug === slug);
234
+ }
235
+ function findPublicationByHost(host) {
236
+ const cleanHost = host.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
237
+ return localPublications.find((p) => {
238
+ const pubHost = p.url.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
239
+ return pubHost === cleanHost || cleanHost.startsWith(`${p.slug}.`);
240
+ });
241
+ }
242
+ function getNetworkPublicationUrl(slug) {
243
+ const pub = findPublicationBySlug(slug);
244
+ if (!pub) return null;
245
+ return decorateNetworkUrl(pub.url + "/");
246
+ }
247
+ var NETWORK_HOSTNAMES = new Set(
248
+ localPublications.map((p) => {
249
+ try {
250
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
251
+ } catch {
252
+ return `${p.slug}.com`;
253
+ }
254
+ })
255
+ );
256
+ function isNetworkHostname(hostname) {
257
+ if (!hostname) return false;
258
+ const clean = hostname.replace(/^www\./, "").toLowerCase();
259
+ return NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
260
+ }
261
+
262
+ export { COOKIE_CATEGORIES, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, isNetworkHostname, listedPublications, localPublications, recordConsentToCompliance, setupNetworkConsentDelegation };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-QWK6G2UF.js';
2
+ import { getCookieConsent, DEFAULT_PREFERENCES_ACCEPTED, COOKIE_CATEGORIES, setCookieConsent, recordConsentToCompliance, DEFAULT_PREFERENCES_REJECTED } from './chunk-2IYYEFLD.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { checkUrlConsentBridge, hasConsented, isGeoConsentRequired, queryNetworkConsentHub, DEFAULT_CONSENT_HUB_URL, acceptAllCookies, rejectOptionalCookies, COOKIE_CONSENT_EVENT, recordConsentToCompliance, setNetworkConsentHub } from './chunk-QWK6G2UF.js';
2
+ import { setupNetworkConsentDelegation, checkUrlConsentBridge, hasConsented, isGeoConsentRequired, queryNetworkConsentHub, DEFAULT_CONSENT_HUB_URL, acceptAllCookies, rejectOptionalCookies, COOKIE_CONSENT_EVENT, recordConsentToCompliance, setNetworkConsentHub } from './chunk-2IYYEFLD.js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -18,6 +18,7 @@ function CookieConsentBanner({
18
18
  const [visible, setVisible] = useState(false);
19
19
  useEffect(() => {
20
20
  setMounted(true);
21
+ setupNetworkConsentDelegation();
21
22
  if (checkUrlConsentBridge()) {
22
23
  setVisible(false);
23
24
  return;
@@ -1,13 +1,22 @@
1
1
  "use client";
2
- import { listedPublications } from './chunk-5W4EMBFY.js';
3
- import { decorateNetworkUrl } from './chunk-QWK6G2UF.js';
4
- import { useState, useId, useRef } from 'react';
2
+ import { setupNetworkConsentDelegation, COOKIE_CONSENT_EVENT, listedPublications, decorateNetworkUrl } from './chunk-2IYYEFLD.js';
3
+ import { useState, useId, useRef, useEffect } from 'react';
5
4
  import { jsx, jsxs } from 'react/jsx-runtime';
6
5
 
7
6
  function LocalNetworkBar({ currentSlug, className = "", style }) {
8
7
  const [open, setOpen] = useState(false);
8
+ const [, setConsentVersion] = useState(0);
9
9
  const id = useId();
10
10
  const button = useRef(null);
11
+ useEffect(() => {
12
+ setupNetworkConsentDelegation();
13
+ const handleConsent = () => setConsentVersion((v) => v + 1);
14
+ window.addEventListener(COOKIE_CONSENT_EVENT, handleConsent);
15
+ return () => window.removeEventListener(COOKIE_CONSENT_EVENT, handleConsent);
16
+ }, []);
17
+ const prepareLink = (e, pubUrl) => {
18
+ e.currentTarget.href = decorateNetworkUrl(pubUrl + "/");
19
+ };
11
20
  return /* @__PURE__ */ jsx(
12
21
  "nav",
13
22
  {
@@ -45,6 +54,8 @@ function LocalNetworkBar({ currentSlug, className = "", style }) {
45
54
  {
46
55
  href: targetUrl,
47
56
  "aria-current": isCurrent ? "true" : void 0,
57
+ onMouseDown: (e) => !isCurrent && prepareLink(e, publication.url),
58
+ onClick: (e) => !isCurrent && prepareLink(e, publication.url),
48
59
  children: publication.name
49
60
  }
50
61
  ) }, publication.slug);
@@ -5,9 +5,140 @@ var react = require('react');
5
5
  var compliance = require('@scalemule/compliance');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
+ // src/network.ts
9
+ var localPublications = [
10
+ {
11
+ slug: "bayareachronicle",
12
+ name: "Bay Area Chronicle",
13
+ url: "https://bayareachronicle.com",
14
+ area: "San Francisco Bay Area",
15
+ description: "The parent home for our local community publications."
16
+ },
17
+ {
18
+ slug: "concordchronicle",
19
+ name: "Concord Chronicle",
20
+ url: "https://concordchronicle.com",
21
+ area: "Concord",
22
+ description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
23
+ },
24
+ {
25
+ slug: "walnutcreektimes",
26
+ name: "Walnut Creek Times",
27
+ url: "https://walnutcreektimes.com",
28
+ area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
29
+ description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
30
+ },
31
+ {
32
+ slug: "lamorindapost",
33
+ name: "Lamorinda Post",
34
+ url: "https://lamorindapost.com",
35
+ area: "Orinda \xB7 Lafayette \xB7 Moraga",
36
+ description: "A new home for the neighbors, businesses and community stories of Lamorinda."
37
+ },
38
+ {
39
+ slug: "alamoweekly",
40
+ name: "Alamo Weekly",
41
+ url: "https://alamoweekly.com",
42
+ area: "Alamo",
43
+ description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
44
+ },
45
+ {
46
+ slug: "danvilletimes",
47
+ name: "Danville Times",
48
+ url: "https://danvilletimes.com",
49
+ area: "Danville",
50
+ description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
51
+ },
52
+ {
53
+ slug: "sanramontimes",
54
+ name: "San Ramon Times",
55
+ url: "https://sanramontimes.com",
56
+ area: "San Ramon",
57
+ description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
58
+ },
59
+ {
60
+ slug: "diablovalleynews",
61
+ name: "Diablo Valley News",
62
+ url: "https://diablovalleynews.com",
63
+ area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
64
+ description: "Local stories about the people, businesses and community life of the Diablo Valley."
65
+ },
66
+ {
67
+ slug: "trivalleyweekly",
68
+ name: "Tri-Valley Weekly",
69
+ url: "https://trivalleyweekly.com",
70
+ area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
71
+ description: "Local stories about the businesses, people and community life of the Tri-Valley."
72
+ },
73
+ {
74
+ slug: "eastbaypost",
75
+ name: "East Bay Post",
76
+ url: "https://eastbaypost.com",
77
+ area: "The East Bay",
78
+ description: "A new publication connecting the people and neighborhoods of the East Bay."
79
+ },
80
+ {
81
+ slug: "napagate",
82
+ name: "Napa Gate",
83
+ url: "https://napagate.com",
84
+ area: "Napa Valley",
85
+ description: "A new local publication for the people, places and everyday life of Napa Valley.",
86
+ listed: false
87
+ }
88
+ ];
89
+ var listedPublications = localPublications.filter(
90
+ (publication) => !("listed" in publication) || publication.listed
91
+ );
92
+ listedPublications.filter(
93
+ (publication) => publication.slug !== "bayareachronicle"
94
+ );
95
+ var NETWORK_HOSTNAMES = new Set(
96
+ localPublications.map((p) => {
97
+ try {
98
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
99
+ } catch {
100
+ return `${p.slug}.com`;
101
+ }
102
+ })
103
+ );
104
+ function isNetworkHostname(hostname) {
105
+ if (!hostname) return false;
106
+ const clean = hostname.replace(/^www\./, "").toLowerCase();
107
+ return NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
108
+ }
109
+
110
+ // src/cookies.ts
8
111
  async function recordConsentToCompliance(record, options) {
9
112
  return compliance.recordConsentToCompliance(record, options);
10
113
  }
114
+ var delegationInstalled = false;
115
+ function setupNetworkConsentDelegation() {
116
+ if (typeof window === "undefined" || delegationInstalled) return;
117
+ delegationInstalled = true;
118
+ document.addEventListener(
119
+ "click",
120
+ (e) => {
121
+ const target = e.target;
122
+ const anchor = target?.closest("a");
123
+ if (!anchor || !anchor.href) return;
124
+ try {
125
+ const url = new URL(anchor.href, window.location.href);
126
+ if (url.protocol !== "http:" && url.protocol !== "https:") return;
127
+ if (isNetworkHostname(url.hostname)) {
128
+ const consent = compliance.getCookieConsent();
129
+ if (consent) {
130
+ const decorated = compliance.decorateNetworkUrl(anchor.href);
131
+ if (decorated !== anchor.href) {
132
+ anchor.href = decorated;
133
+ }
134
+ }
135
+ }
136
+ } catch {
137
+ }
138
+ },
139
+ { capture: true }
140
+ );
141
+ }
11
142
  function CookieConsentBanner({
12
143
  title = "Help Us Improve Our Website with Cookies",
13
144
  text,
@@ -23,6 +154,7 @@ function CookieConsentBanner({
23
154
  const [visible, setVisible] = react.useState(false);
24
155
  react.useEffect(() => {
25
156
  setMounted(true);
157
+ setupNetworkConsentDelegation();
26
158
  if (compliance.checkUrlConsentBridge()) {
27
159
  setVisible(false);
28
160
  return;
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieConsentBanner } from './chunk-5YQYJ2L2.js';
3
- import './chunk-QWK6G2UF.js';
2
+ export { CookieConsentBanner } from './chunk-HTMIFWNA.js';
3
+ import './chunk-2IYYEFLD.js';
@@ -5,6 +5,104 @@ var react = require('react');
5
5
  var compliance = require('@scalemule/compliance');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
+ // src/network.ts
9
+ var localPublications = [
10
+ {
11
+ slug: "bayareachronicle",
12
+ name: "Bay Area Chronicle",
13
+ url: "https://bayareachronicle.com",
14
+ area: "San Francisco Bay Area",
15
+ description: "The parent home for our local community publications."
16
+ },
17
+ {
18
+ slug: "concordchronicle",
19
+ name: "Concord Chronicle",
20
+ url: "https://concordchronicle.com",
21
+ area: "Concord",
22
+ description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
23
+ },
24
+ {
25
+ slug: "walnutcreektimes",
26
+ name: "Walnut Creek Times",
27
+ url: "https://walnutcreektimes.com",
28
+ area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
29
+ description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
30
+ },
31
+ {
32
+ slug: "lamorindapost",
33
+ name: "Lamorinda Post",
34
+ url: "https://lamorindapost.com",
35
+ area: "Orinda \xB7 Lafayette \xB7 Moraga",
36
+ description: "A new home for the neighbors, businesses and community stories of Lamorinda."
37
+ },
38
+ {
39
+ slug: "alamoweekly",
40
+ name: "Alamo Weekly",
41
+ url: "https://alamoweekly.com",
42
+ area: "Alamo",
43
+ description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
44
+ },
45
+ {
46
+ slug: "danvilletimes",
47
+ name: "Danville Times",
48
+ url: "https://danvilletimes.com",
49
+ area: "Danville",
50
+ description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
51
+ },
52
+ {
53
+ slug: "sanramontimes",
54
+ name: "San Ramon Times",
55
+ url: "https://sanramontimes.com",
56
+ area: "San Ramon",
57
+ description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
58
+ },
59
+ {
60
+ slug: "diablovalleynews",
61
+ name: "Diablo Valley News",
62
+ url: "https://diablovalleynews.com",
63
+ area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
64
+ description: "Local stories about the people, businesses and community life of the Diablo Valley."
65
+ },
66
+ {
67
+ slug: "trivalleyweekly",
68
+ name: "Tri-Valley Weekly",
69
+ url: "https://trivalleyweekly.com",
70
+ area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
71
+ description: "Local stories about the businesses, people and community life of the Tri-Valley."
72
+ },
73
+ {
74
+ slug: "eastbaypost",
75
+ name: "East Bay Post",
76
+ url: "https://eastbaypost.com",
77
+ area: "The East Bay",
78
+ description: "A new publication connecting the people and neighborhoods of the East Bay."
79
+ },
80
+ {
81
+ slug: "napagate",
82
+ name: "Napa Gate",
83
+ url: "https://napagate.com",
84
+ area: "Napa Valley",
85
+ description: "A new local publication for the people, places and everyday life of Napa Valley.",
86
+ listed: false
87
+ }
88
+ ];
89
+ var listedPublications = localPublications.filter(
90
+ (publication) => !("listed" in publication) || publication.listed
91
+ );
92
+ listedPublications.filter(
93
+ (publication) => publication.slug !== "bayareachronicle"
94
+ );
95
+ new Set(
96
+ localPublications.map((p) => {
97
+ try {
98
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
99
+ } catch {
100
+ return `${p.slug}.com`;
101
+ }
102
+ })
103
+ );
104
+
105
+ // src/cookies.ts
8
106
  async function recordConsentToCompliance(record, options) {
9
107
  return compliance.recordConsentToCompliance(record, options);
10
108
  }
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- export { CookieSettingsView } from './chunk-WZ3NQRQO.js';
3
- import './chunk-QWK6G2UF.js';
2
+ export { CookieSettingsView } from './chunk-3MFKPHO3.js';
3
+ import './chunk-2IYYEFLD.js';
package/dist/index.cjs CHANGED
@@ -783,9 +783,156 @@ async function syncPublication(input) {
783
783
  ageMs
784
784
  };
785
785
  }
786
+
787
+ // src/network.ts
788
+ var localPublications = [
789
+ {
790
+ slug: "bayareachronicle",
791
+ name: "Bay Area Chronicle",
792
+ url: "https://bayareachronicle.com",
793
+ area: "San Francisco Bay Area",
794
+ description: "The parent home for our local community publications."
795
+ },
796
+ {
797
+ slug: "concordchronicle",
798
+ name: "Concord Chronicle",
799
+ url: "https://concordchronicle.com",
800
+ area: "Concord",
801
+ description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
802
+ },
803
+ {
804
+ slug: "walnutcreektimes",
805
+ name: "Walnut Creek Times",
806
+ url: "https://walnutcreektimes.com",
807
+ area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
808
+ description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
809
+ },
810
+ {
811
+ slug: "lamorindapost",
812
+ name: "Lamorinda Post",
813
+ url: "https://lamorindapost.com",
814
+ area: "Orinda \xB7 Lafayette \xB7 Moraga",
815
+ description: "A new home for the neighbors, businesses and community stories of Lamorinda."
816
+ },
817
+ {
818
+ slug: "alamoweekly",
819
+ name: "Alamo Weekly",
820
+ url: "https://alamoweekly.com",
821
+ area: "Alamo",
822
+ description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
823
+ },
824
+ {
825
+ slug: "danvilletimes",
826
+ name: "Danville Times",
827
+ url: "https://danvilletimes.com",
828
+ area: "Danville",
829
+ description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
830
+ },
831
+ {
832
+ slug: "sanramontimes",
833
+ name: "San Ramon Times",
834
+ url: "https://sanramontimes.com",
835
+ area: "San Ramon",
836
+ description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
837
+ },
838
+ {
839
+ slug: "diablovalleynews",
840
+ name: "Diablo Valley News",
841
+ url: "https://diablovalleynews.com",
842
+ area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
843
+ description: "Local stories about the people, businesses and community life of the Diablo Valley."
844
+ },
845
+ {
846
+ slug: "trivalleyweekly",
847
+ name: "Tri-Valley Weekly",
848
+ url: "https://trivalleyweekly.com",
849
+ area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
850
+ description: "Local stories about the businesses, people and community life of the Tri-Valley."
851
+ },
852
+ {
853
+ slug: "eastbaypost",
854
+ name: "East Bay Post",
855
+ url: "https://eastbaypost.com",
856
+ area: "The East Bay",
857
+ description: "A new publication connecting the people and neighborhoods of the East Bay."
858
+ },
859
+ {
860
+ slug: "napagate",
861
+ name: "Napa Gate",
862
+ url: "https://napagate.com",
863
+ area: "Napa Valley",
864
+ description: "A new local publication for the people, places and everyday life of Napa Valley.",
865
+ listed: false
866
+ }
867
+ ];
868
+ var listedPublications = localPublications.filter(
869
+ (publication) => !("listed" in publication) || publication.listed
870
+ );
871
+ var communityPublications = listedPublications.filter(
872
+ (publication) => publication.slug !== "bayareachronicle"
873
+ );
874
+ function findPublicationBySlug(slug) {
875
+ return localPublications.find((p) => p.slug === slug);
876
+ }
877
+ function findPublicationByHost(host) {
878
+ const cleanHost = host.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
879
+ return localPublications.find((p) => {
880
+ const pubHost = p.url.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
881
+ return pubHost === cleanHost || cleanHost.startsWith(`${p.slug}.`);
882
+ });
883
+ }
884
+ function getNetworkPublicationUrl(slug) {
885
+ const pub = findPublicationBySlug(slug);
886
+ if (!pub) return null;
887
+ return compliance.decorateNetworkUrl(pub.url + "/");
888
+ }
889
+ var NETWORK_HOSTNAMES = new Set(
890
+ localPublications.map((p) => {
891
+ try {
892
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
893
+ } catch {
894
+ return `${p.slug}.com`;
895
+ }
896
+ })
897
+ );
898
+ function isNetworkHostname(hostname) {
899
+ if (!hostname) return false;
900
+ const clean = hostname.replace(/^www\./, "").toLowerCase();
901
+ return NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
902
+ }
903
+
904
+ // src/cookies.ts
786
905
  async function recordConsentToCompliance(record, options) {
787
906
  return compliance.recordConsentToCompliance(record, options);
788
907
  }
908
+ var delegationInstalled = false;
909
+ function setupNetworkConsentDelegation() {
910
+ if (typeof window === "undefined" || delegationInstalled) return;
911
+ delegationInstalled = true;
912
+ document.addEventListener(
913
+ "click",
914
+ (e) => {
915
+ const target = e.target;
916
+ const anchor = target?.closest("a");
917
+ if (!anchor || !anchor.href) return;
918
+ try {
919
+ const url = new URL(anchor.href, window.location.href);
920
+ if (url.protocol !== "http:" && url.protocol !== "https:") return;
921
+ if (isNetworkHostname(url.hostname)) {
922
+ const consent = compliance.getCookieConsent();
923
+ if (consent) {
924
+ const decorated = compliance.decorateNetworkUrl(anchor.href);
925
+ if (decorated !== anchor.href) {
926
+ anchor.href = decorated;
927
+ }
928
+ }
929
+ }
930
+ } catch {
931
+ }
932
+ },
933
+ { capture: true }
934
+ );
935
+ }
789
936
  var COOKIE_CATEGORIES = [
790
937
  {
791
938
  id: "essential",
@@ -909,6 +1056,7 @@ function CookieConsentBanner({
909
1056
  const [visible, setVisible] = react.useState(false);
910
1057
  react.useEffect(() => {
911
1058
  setMounted(true);
1059
+ setupNetworkConsentDelegation();
912
1060
  if (compliance.checkUrlConsentBridge()) {
913
1061
  setVisible(false);
914
1062
  return;
@@ -1550,113 +1698,20 @@ function ThemeToggle({
1550
1698
  ] })
1551
1699
  ] });
1552
1700
  }
1553
-
1554
- // src/network.ts
1555
- var localPublications = [
1556
- {
1557
- slug: "bayareachronicle",
1558
- name: "Bay Area Chronicle",
1559
- url: "https://bayareachronicle.com",
1560
- area: "San Francisco Bay Area",
1561
- description: "The parent home for our local community publications."
1562
- },
1563
- {
1564
- slug: "concordchronicle",
1565
- name: "Concord Chronicle",
1566
- url: "https://concordchronicle.com",
1567
- area: "Concord",
1568
- description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
1569
- },
1570
- {
1571
- slug: "walnutcreektimes",
1572
- name: "Walnut Creek Times",
1573
- url: "https://walnutcreektimes.com",
1574
- area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
1575
- description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
1576
- },
1577
- {
1578
- slug: "lamorindapost",
1579
- name: "Lamorinda Post",
1580
- url: "https://lamorindapost.com",
1581
- area: "Orinda \xB7 Lafayette \xB7 Moraga",
1582
- description: "A new home for the neighbors, businesses and community stories of Lamorinda."
1583
- },
1584
- {
1585
- slug: "alamoweekly",
1586
- name: "Alamo Weekly",
1587
- url: "https://alamoweekly.com",
1588
- area: "Alamo",
1589
- description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
1590
- },
1591
- {
1592
- slug: "danvilletimes",
1593
- name: "Danville Times",
1594
- url: "https://danvilletimes.com",
1595
- area: "Danville",
1596
- description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
1597
- },
1598
- {
1599
- slug: "sanramontimes",
1600
- name: "San Ramon Times",
1601
- url: "https://sanramontimes.com",
1602
- area: "San Ramon",
1603
- description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
1604
- },
1605
- {
1606
- slug: "diablovalleynews",
1607
- name: "Diablo Valley News",
1608
- url: "https://diablovalleynews.com",
1609
- area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
1610
- description: "Local stories about the people, businesses and community life of the Diablo Valley."
1611
- },
1612
- {
1613
- slug: "trivalleyweekly",
1614
- name: "Tri-Valley Weekly",
1615
- url: "https://trivalleyweekly.com",
1616
- area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
1617
- description: "Local stories about the businesses, people and community life of the Tri-Valley."
1618
- },
1619
- {
1620
- slug: "eastbaypost",
1621
- name: "East Bay Post",
1622
- url: "https://eastbaypost.com",
1623
- area: "The East Bay",
1624
- description: "A new publication connecting the people and neighborhoods of the East Bay."
1625
- },
1626
- {
1627
- slug: "napagate",
1628
- name: "Napa Gate",
1629
- url: "https://napagate.com",
1630
- area: "Napa Valley",
1631
- description: "A new local publication for the people, places and everyday life of Napa Valley.",
1632
- listed: false
1633
- }
1634
- ];
1635
- var listedPublications = localPublications.filter(
1636
- (publication) => !("listed" in publication) || publication.listed
1637
- );
1638
- var communityPublications = listedPublications.filter(
1639
- (publication) => publication.slug !== "bayareachronicle"
1640
- );
1641
- function findPublicationBySlug(slug) {
1642
- return localPublications.find((p) => p.slug === slug);
1643
- }
1644
- function findPublicationByHost(host) {
1645
- const cleanHost = host.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
1646
- return localPublications.find((p) => {
1647
- const pubHost = p.url.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
1648
- return pubHost === cleanHost || cleanHost.startsWith(`${p.slug}.`);
1649
- });
1650
- }
1651
- function getNetworkPublicationUrl(slug) {
1652
- const pub = findPublicationBySlug(slug);
1653
- if (!pub) return null;
1654
- return compliance.decorateNetworkUrl(pub.url + "/");
1655
- }
1656
1701
  function LocalNetworkBar({ currentSlug, className = "", style }) {
1657
1702
  const [open, setOpen] = react.useState(false);
1703
+ const [, setConsentVersion] = react.useState(0);
1658
1704
  const id = react.useId();
1659
1705
  const button = react.useRef(null);
1706
+ react.useEffect(() => {
1707
+ setupNetworkConsentDelegation();
1708
+ const handleConsent = () => setConsentVersion((v) => v + 1);
1709
+ window.addEventListener(compliance.COOKIE_CONSENT_EVENT, handleConsent);
1710
+ return () => window.removeEventListener(compliance.COOKIE_CONSENT_EVENT, handleConsent);
1711
+ }, []);
1712
+ const prepareLink = (e, pubUrl) => {
1713
+ e.currentTarget.href = compliance.decorateNetworkUrl(pubUrl + "/");
1714
+ };
1660
1715
  return /* @__PURE__ */ jsxRuntime.jsx(
1661
1716
  "nav",
1662
1717
  {
@@ -1694,6 +1749,8 @@ function LocalNetworkBar({ currentSlug, className = "", style }) {
1694
1749
  {
1695
1750
  href: targetUrl,
1696
1751
  "aria-current": isCurrent ? "true" : void 0,
1752
+ onMouseDown: (e) => !isCurrent && prepareLink(e, publication.url),
1753
+ onClick: (e) => !isCurrent && prepareLink(e, publication.url),
1697
1754
  children: publication.name
1698
1755
  }
1699
1756
  ) }, publication.slug);
package/dist/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  export { THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, ThemeToggle, applyTheme, clearTheme, getStoredTheme, getSystemTheme, resolveEffectiveTheme, setTheme, subscribeTheme, themeInitScript } from './chunk-NQD3M7ZY.js';
2
- export { LocalNetworkBar } from './chunk-7TBML5Q7.js';
3
- export { communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './chunk-5W4EMBFY.js';
2
+ export { LocalNetworkBar } from './chunk-QBT66OVE.js';
4
3
  import { definePublication } from './chunk-C7JTEHTH.js';
5
4
  export { BROADSHEET_THEME, FEATURE_NAMES, LAYOUT_COOKIE, acceptRemoteRecord, acceptSnapshot, definePublication, featureEnabled, parseSnapshot, publicationFromSnapshot, publicationRecord, resolveLayout, snapshotFromRecord, syncPublication } from './chunk-C7JTEHTH.js';
6
5
  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';
7
6
  export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup, screenMessage } from './chunk-GB36YM56.js';
8
- export { CookieConsentBanner } from './chunk-5YQYJ2L2.js';
9
- export { CookieSettingsView } from './chunk-WZ3NQRQO.js';
10
- export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, decorateNetworkUrl, getCookieConsent, hasConsented, isCategoryAllowed, isGeoConsentRequired, queryNetworkConsentHub, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from './chunk-QWK6G2UF.js';
7
+ export { CookieConsentBanner } from './chunk-HTMIFWNA.js';
8
+ export { CookieSettingsView } from './chunk-3MFKPHO3.js';
9
+ export { COOKIE_CATEGORIES, COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, DEFAULT_CONSENT_HUB_URL, DEFAULT_PREFERENCES_ACCEPTED, DEFAULT_PREFERENCES_REJECTED, EU_EEA_UK_COUNTRIES, NETWORK_CONSENT_STORAGE_KEY, acceptAllCookies, checkUrlConsentBridge, clearCookieConsent, communityPublications, decorateNetworkUrl, findPublicationByHost, findPublicationBySlug, getCookieConsent, getNetworkPublicationUrl, hasConsented, isCategoryAllowed, isGeoConsentRequired, listedPublications, localPublications, queryNetworkConsentHub, recordConsentToCompliance, rejectOptionalCookies, setCookieConsent, setNetworkConsentHub } from './chunk-2IYYEFLD.js';
11
10
 
12
11
  // src/preset.ts
13
12
  var walnutCreekTimes = definePublication({
@@ -5,6 +5,35 @@ var react = require('react');
5
5
  var compliance = require('@scalemule/compliance');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
+ var delegationInstalled = false;
9
+ function setupNetworkConsentDelegation() {
10
+ if (typeof window === "undefined" || delegationInstalled) return;
11
+ delegationInstalled = true;
12
+ document.addEventListener(
13
+ "click",
14
+ (e) => {
15
+ const target = e.target;
16
+ const anchor = target?.closest("a");
17
+ if (!anchor || !anchor.href) return;
18
+ try {
19
+ const url = new URL(anchor.href, window.location.href);
20
+ if (url.protocol !== "http:" && url.protocol !== "https:") return;
21
+ if (isNetworkHostname(url.hostname)) {
22
+ const consent = compliance.getCookieConsent();
23
+ if (consent) {
24
+ const decorated = compliance.decorateNetworkUrl(anchor.href);
25
+ if (decorated !== anchor.href) {
26
+ anchor.href = decorated;
27
+ }
28
+ }
29
+ }
30
+ } catch {
31
+ }
32
+ },
33
+ { capture: true }
34
+ );
35
+ }
36
+
8
37
  // src/network.ts
9
38
  var localPublications = [
10
39
  {
@@ -92,10 +121,34 @@ var listedPublications = localPublications.filter(
92
121
  listedPublications.filter(
93
122
  (publication) => publication.slug !== "bayareachronicle"
94
123
  );
124
+ var NETWORK_HOSTNAMES = new Set(
125
+ localPublications.map((p) => {
126
+ try {
127
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
128
+ } catch {
129
+ return `${p.slug}.com`;
130
+ }
131
+ })
132
+ );
133
+ function isNetworkHostname(hostname) {
134
+ if (!hostname) return false;
135
+ const clean = hostname.replace(/^www\./, "").toLowerCase();
136
+ return NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
137
+ }
95
138
  function LocalNetworkBar({ currentSlug, className = "", style }) {
96
139
  const [open, setOpen] = react.useState(false);
140
+ const [, setConsentVersion] = react.useState(0);
97
141
  const id = react.useId();
98
142
  const button = react.useRef(null);
143
+ react.useEffect(() => {
144
+ setupNetworkConsentDelegation();
145
+ const handleConsent = () => setConsentVersion((v) => v + 1);
146
+ window.addEventListener(compliance.COOKIE_CONSENT_EVENT, handleConsent);
147
+ return () => window.removeEventListener(compliance.COOKIE_CONSENT_EVENT, handleConsent);
148
+ }, []);
149
+ const prepareLink = (e, pubUrl) => {
150
+ e.currentTarget.href = compliance.decorateNetworkUrl(pubUrl + "/");
151
+ };
99
152
  return /* @__PURE__ */ jsxRuntime.jsx(
100
153
  "nav",
101
154
  {
@@ -133,6 +186,8 @@ function LocalNetworkBar({ currentSlug, className = "", style }) {
133
186
  {
134
187
  href: targetUrl,
135
188
  "aria-current": isCurrent ? "true" : void 0,
189
+ onMouseDown: (e) => !isCurrent && prepareLink(e, publication.url),
190
+ onClick: (e) => !isCurrent && prepareLink(e, publication.url),
136
191
  children: publication.name
137
192
  }
138
193
  ) }, publication.slug);
@@ -1,3 +1,2 @@
1
- export { LocalNetworkBar } from './chunk-7TBML5Q7.js';
2
- import './chunk-5W4EMBFY.js';
3
- import './chunk-QWK6G2UF.js';
1
+ export { LocalNetworkBar } from './chunk-QBT66OVE.js';
2
+ import './chunk-2IYYEFLD.js';
package/dist/network.cjs CHANGED
@@ -106,10 +106,25 @@ function getNetworkPublicationUrl(slug) {
106
106
  if (!pub) return null;
107
107
  return compliance.decorateNetworkUrl(pub.url + "/");
108
108
  }
109
+ var NETWORK_HOSTNAMES = new Set(
110
+ localPublications.map((p) => {
111
+ try {
112
+ return new URL(p.url).hostname.replace(/^www\./, "").toLowerCase();
113
+ } catch {
114
+ return `${p.slug}.com`;
115
+ }
116
+ })
117
+ );
118
+ function isNetworkHostname(hostname) {
119
+ if (!hostname) return false;
120
+ const clean = hostname.replace(/^www\./, "").toLowerCase();
121
+ return NETWORK_HOSTNAMES.has(clean) || clean.endsWith(".bayareachronicle.com") || clean.endsWith(".napsite.com") || clean.endsWith(".site.scalemule.com");
122
+ }
109
123
 
110
124
  exports.communityPublications = communityPublications;
111
125
  exports.findPublicationByHost = findPublicationByHost;
112
126
  exports.findPublicationBySlug = findPublicationBySlug;
113
127
  exports.getNetworkPublicationUrl = getNetworkPublicationUrl;
128
+ exports.isNetworkHostname = isNetworkHostname;
114
129
  exports.listedPublications = listedPublications;
115
130
  exports.localPublications = localPublications;
@@ -19,5 +19,7 @@ declare function findPublicationBySlug(slug: string): PublicationNetworkItem | u
19
19
  declare function findPublicationByHost(host: string): PublicationNetworkItem | undefined;
20
20
  /** Get decorated URL for cross-domain navigation within the network with consent transfer. */
21
21
  declare function getNetworkPublicationUrl(slug: string): string | null;
22
+ /** Check whether a given hostname belongs to any publication in the ScaleMule news network. */
23
+ declare function isNetworkHostname(hostname: string): boolean;
22
24
 
23
- export { type PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications };
25
+ export { type PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, isNetworkHostname, listedPublications, localPublications };
package/dist/network.d.ts CHANGED
@@ -19,5 +19,7 @@ declare function findPublicationBySlug(slug: string): PublicationNetworkItem | u
19
19
  declare function findPublicationByHost(host: string): PublicationNetworkItem | undefined;
20
20
  /** Get decorated URL for cross-domain navigation within the network with consent transfer. */
21
21
  declare function getNetworkPublicationUrl(slug: string): string | null;
22
+ /** Check whether a given hostname belongs to any publication in the ScaleMule news network. */
23
+ declare function isNetworkHostname(hostname: string): boolean;
22
24
 
23
- export { type PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications };
25
+ export { type PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, isNetworkHostname, listedPublications, localPublications };
package/dist/network.js CHANGED
@@ -1,2 +1 @@
1
- export { communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './chunk-5W4EMBFY.js';
2
- import './chunk-QWK6G2UF.js';
1
+ export { communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, isNetworkHostname, listedPublications, localPublications } from './chunk-2IYYEFLD.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/news-publication",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
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",
@@ -123,6 +123,6 @@
123
123
  "registry": "https://registry.npmjs.org/"
124
124
  },
125
125
  "dependencies": {
126
- "@scalemule/compliance": "^0.0.2"
126
+ "@scalemule/compliance": "^0.0.5"
127
127
  }
128
128
  }
@@ -1,106 +0,0 @@
1
- import { decorateNetworkUrl } from './chunk-QWK6G2UF.js';
2
-
3
- // src/network.ts
4
- var localPublications = [
5
- {
6
- slug: "bayareachronicle",
7
- name: "Bay Area Chronicle",
8
- url: "https://bayareachronicle.com",
9
- area: "San Francisco Bay Area",
10
- description: "The parent home for our local community publications."
11
- },
12
- {
13
- slug: "concordchronicle",
14
- name: "Concord Chronicle",
15
- url: "https://concordchronicle.com",
16
- area: "Concord",
17
- description: "A new place for Concord\u2019s local stories, independent businesses and community voices."
18
- },
19
- {
20
- slug: "walnutcreektimes",
21
- name: "Walnut Creek Times",
22
- url: "https://walnutcreektimes.com",
23
- area: "Walnut Creek \xB7 Rossmoor \xB7 Saranap \xB7 Pleasant Hill",
24
- description: "A new place for Walnut Creek\u2019s local stories, independent businesses and community voices."
25
- },
26
- {
27
- slug: "lamorindapost",
28
- name: "Lamorinda Post",
29
- url: "https://lamorindapost.com",
30
- area: "Orinda \xB7 Lafayette \xB7 Moraga",
31
- description: "A new home for the neighbors, businesses and community stories of Lamorinda."
32
- },
33
- {
34
- slug: "alamoweekly",
35
- name: "Alamo Weekly",
36
- url: "https://alamoweekly.com",
37
- area: "Alamo",
38
- description: "A new place for Alamo\u2019s local stories, independent businesses and community voices."
39
- },
40
- {
41
- slug: "danvilletimes",
42
- name: "Danville Times",
43
- url: "https://danvilletimes.com",
44
- area: "Danville",
45
- description: "A new place for Danville\u2019s local stories, independent businesses and community voices."
46
- },
47
- {
48
- slug: "sanramontimes",
49
- name: "San Ramon Times",
50
- url: "https://sanramontimes.com",
51
- area: "San Ramon",
52
- description: "A new place for San Ramon\u2019s local stories, independent businesses and community voices."
53
- },
54
- {
55
- slug: "diablovalleynews",
56
- name: "Diablo Valley News",
57
- url: "https://diablovalleynews.com",
58
- area: "Concord \xB7 Walnut Creek \xB7 Pleasant Hill \xB7 Alamo \xB7 Danville \xB7 San Ramon",
59
- description: "Local stories about the people, businesses and community life of the Diablo Valley."
60
- },
61
- {
62
- slug: "trivalleyweekly",
63
- name: "Tri-Valley Weekly",
64
- url: "https://trivalleyweekly.com",
65
- area: "Alamo \xB7 Danville \xB7 San Ramon \xB7 Dublin \xB7 Pleasanton \xB7 Livermore",
66
- description: "Local stories about the businesses, people and community life of the Tri-Valley."
67
- },
68
- {
69
- slug: "eastbaypost",
70
- name: "East Bay Post",
71
- url: "https://eastbaypost.com",
72
- area: "The East Bay",
73
- description: "A new publication connecting the people and neighborhoods of the East Bay."
74
- },
75
- {
76
- slug: "napagate",
77
- name: "Napa Gate",
78
- url: "https://napagate.com",
79
- area: "Napa Valley",
80
- description: "A new local publication for the people, places and everyday life of Napa Valley.",
81
- listed: false
82
- }
83
- ];
84
- var listedPublications = localPublications.filter(
85
- (publication) => !("listed" in publication) || publication.listed
86
- );
87
- var communityPublications = listedPublications.filter(
88
- (publication) => publication.slug !== "bayareachronicle"
89
- );
90
- function findPublicationBySlug(slug) {
91
- return localPublications.find((p) => p.slug === slug);
92
- }
93
- function findPublicationByHost(host) {
94
- const cleanHost = host.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
95
- return localPublications.find((p) => {
96
- const pubHost = p.url.replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
97
- return pubHost === cleanHost || cleanHost.startsWith(`${p.slug}.`);
98
- });
99
- }
100
- function getNetworkPublicationUrl(slug) {
101
- const pub = findPublicationBySlug(slug);
102
- if (!pub) return null;
103
- return decorateNetworkUrl(pub.url + "/");
104
- }
105
-
106
- export { communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications };