@scalemule/news-publication 0.0.16 → 0.0.18

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.
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);
@@ -1702,6 +1759,56 @@ function LocalNetworkBar({ currentSlug, className = "", style }) {
1702
1759
  }
1703
1760
  );
1704
1761
  }
1762
+ function ArticleProvenance({ metadata }) {
1763
+ const origin = metadata?.reporting?.canonical_origin || metadata?.editorial_origin;
1764
+ const reporting = metadata?.reporting;
1765
+ const label = origin === "COMMENTARY" ? "Commentary" : origin === "ANALYSIS" ? "Analysis" : null;
1766
+ const sources = readerSources(metadata?.citations);
1767
+ const related = readerSources(reporting?.related_links?.map((url) => ({ url })));
1768
+ const credit = reporting?.credit?.trim();
1769
+ const byline = reporting?.original_byline?.trim();
1770
+ const wire = reporting?.wire_credit?.trim();
1771
+ if (!label && !credit && !byline && !wire && !sources.length && !related.length) return null;
1772
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { "aria-label": "About this reporting", className: "article-provenance", children: [
1773
+ label && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "article-provenance__label", children: label }),
1774
+ (credit || byline) && /* @__PURE__ */ jsxRuntime.jsx("p", { children: [credit, byline].filter(Boolean).join(" \xB7 ") }),
1775
+ wire && /* @__PURE__ */ jsxRuntime.jsx("p", { children: wire }),
1776
+ !!sources.length && /* @__PURE__ */ jsxRuntime.jsxs("details", { children: [
1777
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Sources" }),
1778
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: sources.map((source) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: source.url, rel: "noopener noreferrer", target: "_blank", children: source.title || source.publisher || new URL(source.url).hostname }) }, source.url)) })
1779
+ ] }),
1780
+ !!related.length && /* @__PURE__ */ jsxRuntime.jsxs("details", { children: [
1781
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Related reporting" }),
1782
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: related.map((link) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: link.url, rel: "noopener noreferrer", children: [
1783
+ new URL(link.url).hostname,
1784
+ new URL(link.url).pathname
1785
+ ] }) }, link.url)) })
1786
+ ] })
1787
+ ] });
1788
+ }
1789
+ function readerSources(input) {
1790
+ if (!Array.isArray(input)) return [];
1791
+ const seen = /* @__PURE__ */ new Set();
1792
+ const output = [];
1793
+ for (const value of input) {
1794
+ if (!value || typeof value.url !== "string") continue;
1795
+ try {
1796
+ const url = new URL(value.url);
1797
+ if (url.protocol !== "https:" || url.username || url.password || seen.has(url.href)) continue;
1798
+ seen.add(url.href);
1799
+ const text = (raw, limit) => {
1800
+ if (typeof raw !== "string") return void 0;
1801
+ const trimmed = raw.trim();
1802
+ return trimmed ? Array.from(trimmed).slice(0, limit).join("") : void 0;
1803
+ };
1804
+ output.push({ url: url.href, title: text(value.title, 500), publisher: text(value.publisher, 255) });
1805
+ if (output.length === 5) break;
1806
+ } catch {
1807
+ continue;
1808
+ }
1809
+ }
1810
+ return output;
1811
+ }
1705
1812
 
1706
1813
  Object.defineProperty(exports, "COOKIE_CONSENT_BACKUP_KEY", {
1707
1814
  enumerable: true,
@@ -1789,6 +1896,7 @@ Object.defineProperty(exports, "setNetworkConsentHub", {
1789
1896
  });
1790
1897
  exports.ANALYTICS_COOKIE = ANALYTICS_COOKIE;
1791
1898
  exports.ANALYTICS_VERSION = ANALYTICS_VERSION;
1899
+ exports.ArticleProvenance = ArticleProvenance;
1792
1900
  exports.BROADSHEET_THEME = BROADSHEET_THEME;
1793
1901
  exports.CLASSIFIED_CATEGORIES = CLASSIFIED_CATEGORIES;
1794
1902
  exports.COOKIE_CATEGORIES = COOKIE_CATEGORIES;
@@ -1873,6 +1981,7 @@ exports.publicationFromSnapshot = publicationFromSnapshot;
1873
1981
  exports.publicationJsonLd = publicationJsonLd;
1874
1982
  exports.publicationMeta = publicationMeta;
1875
1983
  exports.publicationRecord = publicationRecord;
1984
+ exports.readerSources = readerSources;
1876
1985
  exports.recordConsentToCompliance = recordConsentToCompliance;
1877
1986
  exports.referralCode = referralCode;
1878
1987
  exports.resolveCoupon = resolveCoupon;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as PublicationTheme, c as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, d as NearbyUpdate, e as NewsItem, E as EditorialSection, f as NewsArticle, C as CouponOffer } from './snapshot-sHgQHuAF.cjs';
2
- export { A as ArticleCitation, g as CategoryRef, h as CouponSettings, i as CoverImageMetadata, j as FEATURE_NAMES, k as FeatureFlags, l as NewsFeed, m as NewsLayoutId, n as PublicationModules, o as PublicationRecord, P as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, p as StoryDisplay, q as SyncSource, a as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-sHgQHuAF.cjs';
1
+ import { b as PublicationTheme, c as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, d as NearbyUpdate, e as NewsItem, E as EditorialSection, f as NewsArticle, C as CouponOffer } from './snapshot-OZFP3-Up.cjs';
2
+ export { A as ArticleCitation, g as CategoryRef, h as CouponSettings, i as CoverImageMetadata, j as FEATURE_NAMES, k as FeatureFlags, l as NewsFeed, m as NewsLayoutId, n as PublicationModules, o as PublicationRecord, P as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, p as StoryDisplay, q as SyncSource, a as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-OZFP3-Up.cjs';
3
3
  export { M as MEET_ACTIVITIES, b as MeetActivity, c as MeetupInput, a as PreparedMeetup, P as PublicPlace, d as activityLabel, e as canHostMeetup, i as isAdult, p as prepareMeetup, s as screenMessage } from './meet-RWmfj3I3.cjs';
4
4
  export { b as CLASSIFIED_CATEGORIES, c as ClassifiedCategory, C as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, d as PreparedClassified, e as PreparedJob, P as PublicDeal, f as PublicDealOffer, a as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-29i5w4LS.cjs';
5
5
  import { CookieCategoryDefinition, CookieConsentRecord } from '@scalemule/compliance';
@@ -9,7 +9,7 @@ export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-v
9
9
  export { ThemeToggle, ThemeToggleProps } from './theme-toggle.cjs';
10
10
  export { PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './network.cjs';
11
11
  export { LocalNetworkBar, LocalNetworkBarProps } from './network-bar.cjs';
12
- import 'react';
12
+ import * as react from 'react';
13
13
 
14
14
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
15
15
  declare const BROADSHEET_THEME: PublicationTheme;
@@ -292,4 +292,12 @@ declare function subscribeTheme(listener: () => void): () => void;
292
292
  */
293
293
  declare function themeInitScript(): string;
294
294
 
295
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CouponOffer, 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, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
295
+ /** A reader projection, never an internal reporting package. */
296
+ declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): react.JSX.Element | null;
297
+ declare function readerSources(input: unknown): {
298
+ url: string;
299
+ title?: string;
300
+ publisher?: string;
301
+ }[];
302
+
303
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CouponOffer, 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, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as PublicationTheme, c as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, d as NearbyUpdate, e as NewsItem, E as EditorialSection, f as NewsArticle, C as CouponOffer } from './snapshot-sHgQHuAF.js';
2
- export { A as ArticleCitation, g as CategoryRef, h as CouponSettings, i as CoverImageMetadata, j as FEATURE_NAMES, k as FeatureFlags, l as NewsFeed, m as NewsLayoutId, n as PublicationModules, o as PublicationRecord, P as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, p as StoryDisplay, q as SyncSource, a as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-sHgQHuAF.js';
1
+ import { b as PublicationTheme, c as PublicationInput, N as NewsPublication, F as FeatureName, H as HomepageSelection, d as NearbyUpdate, e as NewsItem, E as EditorialSection, f as NewsArticle, C as CouponOffer } from './snapshot-OZFP3-Up.js';
2
+ export { A as ArticleCitation, g as CategoryRef, h as CouponSettings, i as CoverImageMetadata, j as FEATURE_NAMES, k as FeatureFlags, l as NewsFeed, m as NewsLayoutId, n as PublicationModules, o as PublicationRecord, P as PublicationSnapshot, R as ReferralSettings, S as SnapshotHooks, p as StoryDisplay, q as SyncSource, a as SyncedPublication, T as TagRef, r as acceptRemoteRecord, s as acceptSnapshot, t as parseSnapshot, u as publicationFromSnapshot, v as publicationRecord, w as snapshotFromRecord, x as syncPublication } from './snapshot-OZFP3-Up.js';
3
3
  export { M as MEET_ACTIVITIES, b as MeetActivity, c as MeetupInput, a as PreparedMeetup, P as PublicPlace, d as activityLabel, e as canHostMeetup, i as isAdult, p as prepareMeetup, s as screenMessage } from './meet-RWmfj3I3.js';
4
4
  export { b as CLASSIFIED_CATEGORIES, c as ClassifiedCategory, C as ClassifiedListing, E as EmploymentType, J as JOB_EMPLOYMENT_TYPES, d as PreparedClassified, e as PreparedJob, P as PublicDeal, f as PublicDealOffer, a as PublicJob, g as categoryLabel, h as classifiedPath, i as classifiedSearchPath, j as clipChars, k as dealDollars, l as dealGoPath, m as dealsFeedPath, n as employmentLabel, o as isClassifiedCategory, p as isEmploymentType, q as jobEmployerBody, r as jobOpeningBody, s as jobPostingBody, t as prepareClassified, u as prepareJobPost, v as publicJobPath, w as publicJobsPath } from './marketplace-29i5w4LS.js';
5
5
  import { CookieCategoryDefinition, CookieConsentRecord } from '@scalemule/compliance';
@@ -9,7 +9,7 @@ export { CookieSettingsView, CookieSettingsViewProps } from './cookie-settings-v
9
9
  export { ThemeToggle, ThemeToggleProps } from './theme-toggle.js';
10
10
  export { PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './network.js';
11
11
  export { LocalNetworkBar, LocalNetworkBarProps } from './network-bar.js';
12
- import 'react';
12
+ import * as react from 'react';
13
13
 
14
14
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
15
15
  declare const BROADSHEET_THEME: PublicationTheme;
@@ -292,4 +292,12 @@ declare function subscribeTheme(listener: () => void): () => void;
292
292
  */
293
293
  declare function themeInitScript(): string;
294
294
 
295
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CouponOffer, 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, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
295
+ /** A reader projection, never an internal reporting package. */
296
+ declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): react.JSX.Element | null;
297
+ declare function readerSources(input: unknown): {
298
+ url: string;
299
+ title?: string;
300
+ publisher?: string;
301
+ }[];
302
+
303
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CouponOffer, 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, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, clearTheme, composeEdition, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
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';
10
+ import { jsxs, jsx } from 'react/jsx-runtime';
11
11
 
12
12
  // src/preset.ts
13
13
  var walnutCreekTimes = definePublication({
@@ -440,5 +440,55 @@ function referralCode(publication, url) {
440
440
  return null;
441
441
  }
442
442
  }
443
+ function ArticleProvenance({ metadata }) {
444
+ const origin = metadata?.reporting?.canonical_origin || metadata?.editorial_origin;
445
+ const reporting = metadata?.reporting;
446
+ const label = origin === "COMMENTARY" ? "Commentary" : origin === "ANALYSIS" ? "Analysis" : null;
447
+ const sources = readerSources(metadata?.citations);
448
+ const related = readerSources(reporting?.related_links?.map((url) => ({ url })));
449
+ const credit = reporting?.credit?.trim();
450
+ const byline = reporting?.original_byline?.trim();
451
+ const wire = reporting?.wire_credit?.trim();
452
+ if (!label && !credit && !byline && !wire && !sources.length && !related.length) return null;
453
+ return /* @__PURE__ */ jsxs("aside", { "aria-label": "About this reporting", className: "article-provenance", children: [
454
+ label && /* @__PURE__ */ jsx("p", { className: "article-provenance__label", children: label }),
455
+ (credit || byline) && /* @__PURE__ */ jsx("p", { children: [credit, byline].filter(Boolean).join(" \xB7 ") }),
456
+ wire && /* @__PURE__ */ jsx("p", { children: wire }),
457
+ !!sources.length && /* @__PURE__ */ jsxs("details", { children: [
458
+ /* @__PURE__ */ jsx("summary", { children: "Sources" }),
459
+ /* @__PURE__ */ jsx("ul", { children: sources.map((source) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: source.url, rel: "noopener noreferrer", target: "_blank", children: source.title || source.publisher || new URL(source.url).hostname }) }, source.url)) })
460
+ ] }),
461
+ !!related.length && /* @__PURE__ */ jsxs("details", { children: [
462
+ /* @__PURE__ */ jsx("summary", { children: "Related reporting" }),
463
+ /* @__PURE__ */ jsx("ul", { children: related.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", { href: link.url, rel: "noopener noreferrer", children: [
464
+ new URL(link.url).hostname,
465
+ new URL(link.url).pathname
466
+ ] }) }, link.url)) })
467
+ ] })
468
+ ] });
469
+ }
470
+ function readerSources(input) {
471
+ if (!Array.isArray(input)) return [];
472
+ const seen = /* @__PURE__ */ new Set();
473
+ const output = [];
474
+ for (const value of input) {
475
+ if (!value || typeof value.url !== "string") continue;
476
+ try {
477
+ const url = new URL(value.url);
478
+ if (url.protocol !== "https:" || url.username || url.password || seen.has(url.href)) continue;
479
+ seen.add(url.href);
480
+ const text = (raw, limit) => {
481
+ if (typeof raw !== "string") return void 0;
482
+ const trimmed = raw.trim();
483
+ return trimmed ? Array.from(trimmed).slice(0, limit).join("") : void 0;
484
+ };
485
+ output.push({ url: url.href, title: text(value.title, 500), publisher: text(value.publisher, 255) });
486
+ if (output.length === 5) break;
487
+ } catch {
488
+ continue;
489
+ }
490
+ }
491
+ return output;
492
+ }
443
493
 
444
- export { ANALYTICS_COOKIE, ANALYTICS_VERSION, EVENTS, NEWS_CACHE, NewsError, SESSION_IDLE_MS, UTM_FIELDS, UUID, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, composeEdition, defaultRevalidateTargets, displayHeadline, edgeCacheControl, editorialSections, feedCacheKey, feedCacheOptions, filterNews, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, referralCode, resolveCoupon, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
494
+ export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, EVENTS, NEWS_CACHE, NewsError, SESSION_IDLE_MS, UTM_FIELDS, UUID, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, composeEdition, defaultRevalidateTargets, displayHeadline, edgeCacheControl, editorialSections, feedCacheKey, feedCacheOptions, filterNews, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, publicHttps, publicUrl, publicationJsonLd, publicationMeta, readerSources, referralCode, resolveCoupon, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
@@ -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 };