@scalemule/news-publication 0.0.17 → 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/README.md CHANGED
@@ -72,3 +72,16 @@ The React front page still lives in the Walnut Creek Times repository. This pack
72
72
  ## Versioning
73
73
 
74
74
  Patch only (`0.0.x` or `0.1.x`) until a stable release is explicitly approved. Publishing is manual: `npm publish` from this directory.
75
+
76
+
77
+ ### Reporting credit and sources
78
+
79
+ `ArticleProvenance` renders the public reporting projection: analysis/commentary labels, network and wire credit, and up to five deduplicated source links. It accepts `NewsArticle.metadata` and never renders internal claim mappings, reporter notes or source artifacts. Syndicated commentary keeps its canonical commentary label.
80
+
81
+ ```tsx
82
+ import { ArticleProvenance } from '@scalemule/news-publication'
83
+
84
+ <ArticleProvenance metadata={article.metadata} />
85
+ ```
86
+
87
+ The component inherits the publication's typography. Optional styling hooks are `article-provenance` and `article-provenance__label`. `readerSources()` is available when an existing publication component owns source-list rendering.
package/dist/index.cjs CHANGED
@@ -1759,6 +1759,56 @@ function LocalNetworkBar({ currentSlug, className = "", style }) {
1759
1759
  }
1760
1760
  );
1761
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
+ }
1762
1812
 
1763
1813
  Object.defineProperty(exports, "COOKIE_CONSENT_BACKUP_KEY", {
1764
1814
  enumerable: true,
@@ -1846,6 +1896,7 @@ Object.defineProperty(exports, "setNetworkConsentHub", {
1846
1896
  });
1847
1897
  exports.ANALYTICS_COOKIE = ANALYTICS_COOKIE;
1848
1898
  exports.ANALYTICS_VERSION = ANALYTICS_VERSION;
1899
+ exports.ArticleProvenance = ArticleProvenance;
1849
1900
  exports.BROADSHEET_THEME = BROADSHEET_THEME;
1850
1901
  exports.CLASSIFIED_CATEGORIES = CLASSIFIED_CATEGORIES;
1851
1902
  exports.COOKIE_CATEGORIES = COOKIE_CATEGORIES;
@@ -1930,6 +1981,7 @@ exports.publicationFromSnapshot = publicationFromSnapshot;
1930
1981
  exports.publicationJsonLd = publicationJsonLd;
1931
1982
  exports.publicationMeta = publicationMeta;
1932
1983
  exports.publicationRecord = publicationRecord;
1984
+ exports.readerSources = readerSources;
1933
1985
  exports.recordConsentToCompliance = recordConsentToCompliance;
1934
1986
  exports.referralCode = referralCode;
1935
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
@@ -7,6 +7,7 @@ export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup,
7
7
  export { CookieConsentBanner } from './chunk-HTMIFWNA.js';
8
8
  export { CookieSettingsView } from './chunk-3MFKPHO3.js';
9
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';
10
11
 
11
12
  // src/preset.ts
12
13
  var walnutCreekTimes = definePublication({
@@ -439,5 +440,55 @@ function referralCode(publication, url) {
439
440
  return null;
440
441
  }
441
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
+ }
442
493
 
443
- 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 };
@@ -145,6 +145,19 @@ type NewsArticle = NewsItem & {
145
145
  duration_ms?: number | null;
146
146
  };
147
147
  metadata?: {
148
+ editorial_origin?: 'UNKNOWN' | 'ORIGINAL_REPORTING' | 'DOCUMENT_REPORTING' | 'PRESS_RELEASE_BASED' | 'NEWS_PICKUP' | 'ANALYSIS' | 'COMMENTARY' | 'LICENSED_WIRE' | 'NETWORK_SYNDICATED' | 'COMMUNITY_SUBMISSION';
149
+ reporting?: {
150
+ edition_id: string;
151
+ story_id: string;
152
+ story_revision: number;
153
+ originating_publication_id: string;
154
+ original_byline?: string;
155
+ credit?: string;
156
+ related_links?: string[];
157
+ wire_credit?: string;
158
+ canonical_origin?: string;
159
+ origin?: string;
160
+ };
148
161
  cover_image?: CoverImageMetadata;
149
162
  citations?: ArticleCitation[];
150
163
  resources?: {
@@ -145,6 +145,19 @@ type NewsArticle = NewsItem & {
145
145
  duration_ms?: number | null;
146
146
  };
147
147
  metadata?: {
148
+ editorial_origin?: 'UNKNOWN' | 'ORIGINAL_REPORTING' | 'DOCUMENT_REPORTING' | 'PRESS_RELEASE_BASED' | 'NEWS_PICKUP' | 'ANALYSIS' | 'COMMENTARY' | 'LICENSED_WIRE' | 'NETWORK_SYNDICATED' | 'COMMUNITY_SUBMISSION';
149
+ reporting?: {
150
+ edition_id: string;
151
+ story_id: string;
152
+ story_revision: number;
153
+ originating_publication_id: string;
154
+ original_byline?: string;
155
+ credit?: string;
156
+ related_links?: string[];
157
+ wire_credit?: string;
158
+ canonical_origin?: string;
159
+ origin?: string;
160
+ };
148
161
  cover_image?: CoverImageMetadata;
149
162
  citations?: ArticleCitation[];
150
163
  resources?: {
@@ -1,4 +1,4 @@
1
- import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-sHgQHuAF.cjs';
1
+ import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-OZFP3-Up.cjs';
2
2
 
3
3
  declare function readSnapshotFile(file: string): Promise<PublicationSnapshot | null>;
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-sHgQHuAF.js';
1
+ import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-OZFP3-Up.js';
2
2
 
3
3
  declare function readSnapshotFile(file: string): Promise<PublicationSnapshot | null>;
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/news-publication",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
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",