@scalemule/news-publication 0.0.44 → 0.0.46
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 +61 -0
- package/dist/index.cjs +268 -107
- package/dist/index.d.cts +110 -4
- package/dist/index.d.ts +110 -4
- package/dist/index.js +267 -108
- package/dist/{snapshot-CxuyMF39.d.ts → snapshot-DmRw8G_B.d.ts} +1 -1
- package/dist/{snapshot-C0IGpt-B.d.cts → snapshot-Dx4Lv-lB.d.cts} +1 -1
- package/dist/snapshot-file.d.cts +2 -2
- package/dist/snapshot-file.d.ts +2 -2
- package/dist/{types-DFDCZHbS.d.cts → types-BJFCWWi0.d.cts} +1 -1
- package/dist/{types-DFDCZHbS.d.ts → types-BJFCWWi0.d.ts} +1 -1
- package/dist/verified-card.d.cts +1 -1
- package/dist/verified-card.d.ts +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -73,6 +73,67 @@ The React front page still lives in the Walnut Creek Times repository. This pack
|
|
|
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
75
|
|
|
76
|
+
## Local briefing foundation
|
|
77
|
+
|
|
78
|
+
`composeLocalBriefing(candidates, options)` is an opt-in, pure selector for an existing
|
|
79
|
+
publication brief. It consumes `BriefingCandidate` projections from an authorized
|
|
80
|
+
server adapter and returns a bounded list using existing `NewsItem` text. It does
|
|
81
|
+
not fetch or generate news, verify claims, store preferences, or change the current
|
|
82
|
+
`composeEdition` path. A server adapter and publication pilot are still required;
|
|
83
|
+
there is no new live BaseNews endpoint in this package.
|
|
84
|
+
|
|
85
|
+
This is an article-backed presentation helper, not the canonical BaseNews schema.
|
|
86
|
+
BaseNews events, issues and opportunities must also work without an article.
|
|
87
|
+
Its eligibility rules apply only to this optional brief. They must never become
|
|
88
|
+
new publishing gates for articles, questionnaires, jokes, jobs, classifieds,
|
|
89
|
+
celebrations or other existing publication features. Their current owner services
|
|
90
|
+
and workflows continue independently, including when intelligence processing is
|
|
91
|
+
unavailable. Underlying capture, trust and agent APIs take priority over UI rollout.
|
|
92
|
+
|
|
93
|
+
Supply the recipient `sm_application_id`, `publication_id`, resolved
|
|
94
|
+
`coverage_area_ids`, and optional explicit community/topic/entity/issue preferences.
|
|
95
|
+
`personalized: false` ignores all preferences. `limit` is a maximum from 1–12
|
|
96
|
+
(default 6); zero useful records yields an empty briefing. Render summaries as
|
|
97
|
+
text, with original reporting links, verification labels and source attribution.
|
|
98
|
+
`why_it_matters` is optional editorial text; reason codes explain selection and
|
|
99
|
+
are not factual claims about an item's consequences.
|
|
100
|
+
|
|
101
|
+
The server must derive scope from authentication, authorize destination grants,
|
|
102
|
+
resolve geographic relationships, enforce evidence/summary redistribution rights,
|
|
103
|
+
and send public, approved projections. Reuse existing Content Intelligence
|
|
104
|
+
classification, coverage profiles, editorial decisions and current evaluation
|
|
105
|
+
receipts in that adapter; this presentation selector does not replace its generic
|
|
106
|
+
relevance engine or editorial ledger. This helper is **not an authorization or
|
|
107
|
+
input-validation boundary**. Do not pass client-supplied trust/approval fields,
|
|
108
|
+
private artifacts, or raw community submissions. Unverified and community-submitted
|
|
109
|
+
information retains those labels even after editorial approval.
|
|
110
|
+
|
|
111
|
+
Each candidate references an existing canonical development, occurrence or reporting
|
|
112
|
+
record via a namespaced `canonical_key`, with its revision. Do not use a whole issue
|
|
113
|
+
ID as the key: separate developments must survive. Include withdrawal/supersession
|
|
114
|
+
revisions when combining cached inventories; the newest revision wins before
|
|
115
|
+
eligibility and ranking. Supply one selected edition per canonical revision;
|
|
116
|
+
conflicting copies of that revision are omitted for reconciliation, not selected
|
|
117
|
+
by input order. A feed lacking tombstones cannot prove that cached records
|
|
118
|
+
are still current. The caller must invalidate caches on corrections and grant
|
|
119
|
+
revocation. Treat personalized HTTP responses as `private, no-store`.
|
|
120
|
+
|
|
121
|
+
Policy 1 uses a civic priority tier, followed by geography × 4, practical value × 3,
|
|
122
|
+
explicit follows × 3, topics × 2, reporting recency and editorial discovery value.
|
|
123
|
+
All seven components and reason codes are returned. Recency describes reporting,
|
|
124
|
+
not event time or urgency. Civic importance comes from editors, not section names.
|
|
125
|
+
Keep a visible link to all important coverage whenever `remaining_important_count`
|
|
126
|
+
is positive. Eligibility still applies to civic items: stale or unauthorized facts
|
|
127
|
+
cannot bypass trust gates.
|
|
128
|
+
|
|
129
|
+
`relevant_until` is required. Confirmed labels also require a public evidence link,
|
|
130
|
+
`last_verified_at` and a future `review_due_at`. Expired review/relevance deadlines
|
|
131
|
+
exclude records; missing freshness information stays `unknown`. No date is inferred
|
|
132
|
+
from `updated_at`, no confidence score promotes verification, and no fallback story
|
|
133
|
+
fills a quota. The entire approved excerpt must end with sentence punctuation and
|
|
134
|
+
fit within 280 Unicode code points; otherwise it is omitted for editorial repair.
|
|
135
|
+
Later qualifications and corrections are never cut off to make a shorter summary.
|
|
136
|
+
|
|
76
137
|
|
|
77
138
|
### Reporting credit and sources
|
|
78
139
|
|
package/dist/index.cjs
CHANGED
|
@@ -73,106 +73,6 @@ function resolveLayout(publication, cookieValue) {
|
|
|
73
73
|
return publication.layout;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// src/preset.ts
|
|
77
|
-
var walnutCreekTimes = definePublication({
|
|
78
|
-
slug: "walnutcreektimes",
|
|
79
|
-
name: "Walnut Creek Times",
|
|
80
|
-
masthead: ["Walnut", "Creek", "Times"],
|
|
81
|
-
region: "Walnut Creek",
|
|
82
|
-
regionLabel: "Walnut Creek",
|
|
83
|
-
towns: ["Walnut Creek", "Rossmoor", "Saranap", "Pleasant Hill"],
|
|
84
|
-
nearbyTowns: ["Martinez", "Concord", "Contra Costa"],
|
|
85
|
-
sections: ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health"],
|
|
86
|
-
description: "Independent local journalism covering civic decisions, downtown commerce, neighborhood life, and community achievements across Walnut Creek, Rossmoor, Saranap, and Pleasant Hill.",
|
|
87
|
-
tagline: "Local stories. Stronger communities.",
|
|
88
|
-
layout: "broadsheet",
|
|
89
|
-
layouts: ["broadsheet"],
|
|
90
|
-
features: { smile: true, jobs: true, classifieds: true },
|
|
91
|
-
modules: { home: ["daily-smile"] }
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// src/cache.ts
|
|
95
|
-
var NEWS_CACHE = {
|
|
96
|
-
feedRevalidateSeconds: 30,
|
|
97
|
-
articleRevalidateSeconds: 60,
|
|
98
|
-
edgeMaxAgeSeconds: 60,
|
|
99
|
-
staleWhileRevalidateSeconds: 300,
|
|
100
|
-
feedTag: "news-feed",
|
|
101
|
-
articleTag: "news-article",
|
|
102
|
-
inventoryTag: "public-news-discovery-v2",
|
|
103
|
-
paths: ["/", "/news"]
|
|
104
|
-
};
|
|
105
|
-
var feedCacheOptions = {
|
|
106
|
-
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
107
|
-
tags: [NEWS_CACHE.feedTag]
|
|
108
|
-
};
|
|
109
|
-
var articleCacheOptions = {
|
|
110
|
-
revalidate: NEWS_CACHE.articleRevalidateSeconds,
|
|
111
|
-
tags: [NEWS_CACHE.articleTag]
|
|
112
|
-
};
|
|
113
|
-
var inventoryCacheOptions = {
|
|
114
|
-
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
115
|
-
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.inventoryTag]
|
|
116
|
-
};
|
|
117
|
-
function edgeCacheControl() {
|
|
118
|
-
return `public, s-maxage=${NEWS_CACHE.edgeMaxAgeSeconds}, stale-while-revalidate=${NEWS_CACHE.staleWhileRevalidateSeconds}`;
|
|
119
|
-
}
|
|
120
|
-
function feedCacheKey(publicationId, before, section) {
|
|
121
|
-
return `${publicationId}:${before ?? ""}:${section ?? ""}`;
|
|
122
|
-
}
|
|
123
|
-
function articleCacheKey(publicationId, slug) {
|
|
124
|
-
return `${publicationId}:${slug}`;
|
|
125
|
-
}
|
|
126
|
-
var NewsError = class extends Error {
|
|
127
|
-
constructor(status, message) {
|
|
128
|
-
super(message);
|
|
129
|
-
this.status = status;
|
|
130
|
-
this.name = "NewsError";
|
|
131
|
-
}
|
|
132
|
-
status;
|
|
133
|
-
};
|
|
134
|
-
function shouldServeLastKnown(error) {
|
|
135
|
-
const status = error instanceof NewsError ? error.status : error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
136
|
-
return status !== 404;
|
|
137
|
-
}
|
|
138
|
-
async function withLastKnown(store, key, load, accept = () => true) {
|
|
139
|
-
try {
|
|
140
|
-
const value = await load();
|
|
141
|
-
if (accept(value)) store.set(key, value);
|
|
142
|
-
return value;
|
|
143
|
-
} catch (error) {
|
|
144
|
-
if (!shouldServeLastKnown(error)) throw error;
|
|
145
|
-
const cached = store.get(key);
|
|
146
|
-
if (cached) return cached;
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function secretsMatch(provided, secret) {
|
|
151
|
-
if (!provided || !secret) return false;
|
|
152
|
-
const left = Array.from(provided);
|
|
153
|
-
const right = Array.from(secret);
|
|
154
|
-
let mismatch = left.length === right.length ? 0 : 1;
|
|
155
|
-
const length = Math.max(left.length, right.length);
|
|
156
|
-
for (let index = 0; index < length; index += 1) {
|
|
157
|
-
mismatch |= (left[index]?.codePointAt(0) ?? 0) ^ (right[index]?.codePointAt(0) ?? 0);
|
|
158
|
-
}
|
|
159
|
-
return mismatch === 0;
|
|
160
|
-
}
|
|
161
|
-
function revalidateDecision(input) {
|
|
162
|
-
if (!secretsMatch(input.providedSecret, input.configuredSecret)) {
|
|
163
|
-
return { status: 401, body: { error: "Unauthorized" } };
|
|
164
|
-
}
|
|
165
|
-
const tag = input.tag?.trim() || `${NEWS_CACHE.feedTag}, ${NEWS_CACHE.articleTag}`;
|
|
166
|
-
const path = input.path?.trim() || NEWS_CACHE.paths.join(", ");
|
|
167
|
-
return { status: 200, body: { revalidated: true, tag, path, now: input.now ?? Date.now() } };
|
|
168
|
-
}
|
|
169
|
-
function defaultRevalidateTargets() {
|
|
170
|
-
return {
|
|
171
|
-
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.articleTag, NEWS_CACHE.inventoryTag],
|
|
172
|
-
paths: [...NEWS_CACHE.paths]
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
76
|
// src/editorial.ts
|
|
177
77
|
var labels = {
|
|
178
78
|
"Local businesses": "Business",
|
|
@@ -190,8 +90,8 @@ var EDITORIAL_SECTION_ORDER = [
|
|
|
190
90
|
"Schools",
|
|
191
91
|
"Opinion"
|
|
192
92
|
];
|
|
193
|
-
function orderedSections(
|
|
194
|
-
return
|
|
93
|
+
function orderedSections(sections2, order = EDITORIAL_SECTION_ORDER) {
|
|
94
|
+
return sections2.filter((section) => section.name !== "Local news").slice().sort((left, right) => {
|
|
195
95
|
const leftRank = order.indexOf(left.name);
|
|
196
96
|
const rightRank = order.indexOf(right.name);
|
|
197
97
|
const leftOrder = leftRank === -1 ? order.length : leftRank;
|
|
@@ -252,12 +152,12 @@ function composeEdition(items, selection = {}, now = Date.now()) {
|
|
|
252
152
|
secondary.push(deferredWithDuplicateImage.shift());
|
|
253
153
|
}
|
|
254
154
|
for (const item of secondary) remaining.splice(remaining.indexOf(item), 1);
|
|
255
|
-
const
|
|
155
|
+
const eligible2 = remaining.filter((item) => {
|
|
256
156
|
const age = now - Date.parse(item.published_at);
|
|
257
157
|
return item.excerpt?.trim() && age >= 0 && age <= 7 * 864e5;
|
|
258
158
|
});
|
|
259
|
-
const selected = (pins.brief_slugs ?? []).flatMap((slug) =>
|
|
260
|
-
const candidates = [...selected, ...editorialSections(
|
|
159
|
+
const selected = (pins.brief_slugs ?? []).flatMap((slug) => eligible2.find((item) => item.slug === slug) ?? []);
|
|
160
|
+
const candidates = [...selected, ...editorialSections(eligible2).map((group) => group.items[0]), ...eligible2];
|
|
261
161
|
const briefIds = /* @__PURE__ */ new Set();
|
|
262
162
|
const briefs = candidates.filter((item) => {
|
|
263
163
|
if (!item || briefIds.has(item.id) || briefIds.size >= 4) return false;
|
|
@@ -266,8 +166,8 @@ function composeEdition(items, selection = {}, now = Date.now()) {
|
|
|
266
166
|
});
|
|
267
167
|
const compact = [...briefs, ...remaining.filter((item) => !briefIds.has(item.id))].slice(0, 4);
|
|
268
168
|
const compactIds = new Set(compact.map((item) => item.id));
|
|
269
|
-
const
|
|
270
|
-
return { lead, secondary, briefs, compact, sections, navigation: editorialSections(inventory) };
|
|
169
|
+
const sections2 = editorialSections(remaining.filter((item) => !compactIds.has(item.id)));
|
|
170
|
+
return { lead, secondary, briefs, compact, sections: sections2, navigation: editorialSections(inventory) };
|
|
271
171
|
}
|
|
272
172
|
function displayHeadline(article, selection = {}) {
|
|
273
173
|
return selection.stories?.[article.slug]?.display_title?.trim() || article.title;
|
|
@@ -432,6 +332,265 @@ function llmsText(publication, canonical) {
|
|
|
432
332
|
].join("\n");
|
|
433
333
|
}
|
|
434
334
|
|
|
335
|
+
// src/briefing.ts
|
|
336
|
+
var LOCAL_BRIEFING_POLICY_VERSION = "1";
|
|
337
|
+
var DAY = 864e5;
|
|
338
|
+
var verifications = /* @__PURE__ */ new Set([
|
|
339
|
+
"unverified",
|
|
340
|
+
"community_submitted",
|
|
341
|
+
"officially_confirmed",
|
|
342
|
+
"independently_verified",
|
|
343
|
+
"disputed"
|
|
344
|
+
]);
|
|
345
|
+
var sections = /* @__PURE__ */ new Set(["important", "around_you", "people", "discover", "participate", "following"]);
|
|
346
|
+
var overlaps = (a, b = []) => a.some((id) => id.trim() !== "" && b.includes(id));
|
|
347
|
+
function timestamp(value) {
|
|
348
|
+
if (!/^\d{4}-\d{2}-\d{2}T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,3})?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(value) || !validCalendarDate(value.slice(0, 10))) return NaN;
|
|
349
|
+
return Date.parse(value);
|
|
350
|
+
}
|
|
351
|
+
function sourcesFor(candidate) {
|
|
352
|
+
const seen = /* @__PURE__ */ new Set();
|
|
353
|
+
return candidate.sources.flatMap((source) => {
|
|
354
|
+
const url = publicHttps(source.url);
|
|
355
|
+
if (!source.id.trim() || !source.publisher.trim() || !source.title.trim() || !url || seen.has(source.id)) return [];
|
|
356
|
+
seen.add(source.id);
|
|
357
|
+
return [{ id: source.id, url, title: source.title, publisher: source.publisher }];
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function eligible(candidate, coverage, now) {
|
|
361
|
+
if (candidate.publication_status !== "published" || candidate.visibility !== "public" || candidate.editorial_approval !== "approved" || candidate.lifecycle !== "current" || !candidate.article.id.trim() || !candidate.article.slug.trim() || !candidate.article.title.trim() || !verifications.has(candidate.verification) || !sections.has(candidate.section) || !overlaps(candidate.geography_ids, coverage)) return false;
|
|
362
|
+
const published = timestamp(candidate.article.published_at);
|
|
363
|
+
const expiry = timestamp(candidate.relevant_until);
|
|
364
|
+
if (!Number.isFinite(published) || published > now || !Number.isFinite(expiry) || expiry <= now) return false;
|
|
365
|
+
const verified = candidate.last_verified_at === null ? null : timestamp(candidate.last_verified_at);
|
|
366
|
+
const review = candidate.review_due_at === null ? null : timestamp(candidate.review_due_at);
|
|
367
|
+
if (verified !== null && (!Number.isFinite(verified) || verified > now)) return false;
|
|
368
|
+
if (review !== null && (!Number.isFinite(review) || review <= now || verified !== null && review <= verified)) return false;
|
|
369
|
+
const confirmed = candidate.verification === "officially_confirmed" || candidate.verification === "independently_verified";
|
|
370
|
+
return !confirmed || verified !== null && review !== null && sourcesFor(candidate).length > 0;
|
|
371
|
+
}
|
|
372
|
+
function projectionKey(candidate) {
|
|
373
|
+
const article = candidate.article;
|
|
374
|
+
return JSON.stringify([
|
|
375
|
+
article.id,
|
|
376
|
+
article.slug,
|
|
377
|
+
article.title,
|
|
378
|
+
article.excerpt,
|
|
379
|
+
article.published_at,
|
|
380
|
+
article.author_name,
|
|
381
|
+
publicHttps(article.canonical_url),
|
|
382
|
+
[...candidate.geography_ids].sort(),
|
|
383
|
+
[...candidate.topic_ids].sort(),
|
|
384
|
+
[...candidate.entity_ids].sort(),
|
|
385
|
+
[...candidate.issue_ids].sort(),
|
|
386
|
+
candidate.section,
|
|
387
|
+
candidate.civic_importance,
|
|
388
|
+
candidate.practical_value,
|
|
389
|
+
candidate.discovery_value,
|
|
390
|
+
candidate.why_it_matters,
|
|
391
|
+
candidate.relevant_until,
|
|
392
|
+
candidate.verification,
|
|
393
|
+
candidate.last_verified_at,
|
|
394
|
+
candidate.review_due_at,
|
|
395
|
+
sourcesFor(candidate).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
|
|
396
|
+
]);
|
|
397
|
+
}
|
|
398
|
+
function composeLocalBriefing(candidates, options) {
|
|
399
|
+
const now = options.now ?? Date.now();
|
|
400
|
+
const limit = options.limit ?? 6;
|
|
401
|
+
if (!options.sm_application_id.trim() || !options.publication_id.trim() || !Number.isFinite(now) || !Number.isFinite(new Date(now).getTime()) || !Number.isInteger(limit) || limit < 1 || limit > 12) {
|
|
402
|
+
throw new RangeError("A briefing requires application/publication scope, a valid time and a limit from 1 to 12");
|
|
403
|
+
}
|
|
404
|
+
const preferences = options.personalized === false ? {} : options.preferences ?? {};
|
|
405
|
+
const personalized = [preferences.community_ids, preferences.topic_ids, preferences.entity_ids, preferences.issue_ids].some((ids) => ids?.some((id) => id.trim() !== ""));
|
|
406
|
+
const newest = /* @__PURE__ */ new Map();
|
|
407
|
+
for (const candidate of candidates) {
|
|
408
|
+
if (candidate.sm_application_id !== options.sm_application_id || candidate.publication_id !== options.publication_id || !candidate.canonical_key.trim() || !Number.isSafeInteger(candidate.revision) || candidate.revision < 1) continue;
|
|
409
|
+
const group = newest.get(candidate.canonical_key);
|
|
410
|
+
if (!group || candidate.revision > group[0].revision) newest.set(candidate.canonical_key, [candidate]);
|
|
411
|
+
else if (candidate.revision === group[0].revision) group.push(candidate);
|
|
412
|
+
}
|
|
413
|
+
const ranked = [];
|
|
414
|
+
for (const group of newest.values()) {
|
|
415
|
+
if (group.some((candidate2) => !eligible(candidate2, options.coverage_area_ids, now))) continue;
|
|
416
|
+
const candidate = group[0];
|
|
417
|
+
if (group.length > 1 && group.some((copy) => projectionKey(copy) !== projectionKey(candidate))) continue;
|
|
418
|
+
const summary = candidate.article.excerpt?.trim() ?? "";
|
|
419
|
+
if (!summary || Array.from(summary).length > 280 || !/[.!?。!?][”’"']?$/u.test(summary)) continue;
|
|
420
|
+
const community = overlaps(candidate.geography_ids, preferences.community_ids);
|
|
421
|
+
const entity = overlaps(candidate.entity_ids, preferences.entity_ids);
|
|
422
|
+
const issue = overlaps(candidate.issue_ids, preferences.issue_ids);
|
|
423
|
+
const topic = overlaps(candidate.topic_ids, preferences.topic_ids);
|
|
424
|
+
const published = timestamp(candidate.article.published_at);
|
|
425
|
+
const age = now - published;
|
|
426
|
+
const important = candidate.civic_importance === "important";
|
|
427
|
+
const relevance = {
|
|
428
|
+
geographic: community ? 2 : 1,
|
|
429
|
+
practical: candidate.practical_value ? 1 : 0,
|
|
430
|
+
relationship: entity || issue ? 1 : 0,
|
|
431
|
+
interest: topic ? 1 : 0,
|
|
432
|
+
temporal: age < DAY ? 2 : age < 7 * DAY ? 1 : 0,
|
|
433
|
+
civic: important ? 1 : 0,
|
|
434
|
+
discovery: candidate.discovery_value ? 1 : 0
|
|
435
|
+
};
|
|
436
|
+
const reasons = [];
|
|
437
|
+
if (important) reasons.push("important_civic_information");
|
|
438
|
+
reasons.push(community ? "selected_community" : "publication_coverage");
|
|
439
|
+
if (candidate.practical_value) reasons.push("practical_value");
|
|
440
|
+
if (entity) reasons.push("following_entity");
|
|
441
|
+
if (issue) reasons.push("following_issue");
|
|
442
|
+
if (topic) reasons.push("selected_topic");
|
|
443
|
+
if (relevance.temporal > 0) reasons.push("recent_reporting");
|
|
444
|
+
if (candidate.discovery_value) reasons.push("local_discovery");
|
|
445
|
+
const article = candidate.article;
|
|
446
|
+
const item = {
|
|
447
|
+
canonical_key: candidate.canonical_key,
|
|
448
|
+
revision: candidate.revision,
|
|
449
|
+
article: {
|
|
450
|
+
id: article.id,
|
|
451
|
+
slug: article.slug,
|
|
452
|
+
title: article.title,
|
|
453
|
+
published_at: article.published_at,
|
|
454
|
+
author_name: article.author_name,
|
|
455
|
+
canonical_url: publicHttps(article.canonical_url) ?? null
|
|
456
|
+
},
|
|
457
|
+
summary,
|
|
458
|
+
section: important ? "important" : entity || issue ? "following" : candidate.section,
|
|
459
|
+
geography_ids: [...new Set(candidate.geography_ids)],
|
|
460
|
+
why_it_matters: candidate.why_it_matters?.trim() || void 0,
|
|
461
|
+
reasons,
|
|
462
|
+
relevance,
|
|
463
|
+
verification: candidate.verification,
|
|
464
|
+
freshness: candidate.last_verified_at !== null && candidate.review_due_at !== null ? "current" : "unknown",
|
|
465
|
+
last_verified_at: candidate.last_verified_at,
|
|
466
|
+
review_due_at: candidate.review_due_at,
|
|
467
|
+
relevant_until: candidate.relevant_until,
|
|
468
|
+
sources: sourcesFor(candidate)
|
|
469
|
+
};
|
|
470
|
+
const score = 4 * relevance.geographic + 3 * relevance.practical + 3 * relevance.relationship + 2 * relevance.interest + relevance.temporal + relevance.discovery;
|
|
471
|
+
ranked.push({ item, score, published, important });
|
|
472
|
+
}
|
|
473
|
+
ranked.sort((a, b) => Number(b.important) - Number(a.important) || b.score - a.score || b.published - a.published || (a.item.canonical_key < b.item.canonical_key ? -1 : a.item.canonical_key > b.item.canonical_key ? 1 : 0));
|
|
474
|
+
const seenArticles = /* @__PURE__ */ new Set();
|
|
475
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
476
|
+
const deduplicated = ranked.filter(({ item }) => {
|
|
477
|
+
const url = item.article.canonical_url;
|
|
478
|
+
if (seenArticles.has(item.article.id) || url && seenUrls.has(url)) return false;
|
|
479
|
+
seenArticles.add(item.article.id);
|
|
480
|
+
if (url) seenUrls.add(url);
|
|
481
|
+
return true;
|
|
482
|
+
});
|
|
483
|
+
return {
|
|
484
|
+
schema_version: 1,
|
|
485
|
+
policy_version: LOCAL_BRIEFING_POLICY_VERSION,
|
|
486
|
+
publication_id: options.publication_id,
|
|
487
|
+
generated_at: new Date(now).toISOString(),
|
|
488
|
+
personalized,
|
|
489
|
+
items: deduplicated.slice(0, limit).map(({ item }) => item),
|
|
490
|
+
remaining_important_count: deduplicated.slice(limit).filter(({ important }) => important).length
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/preset.ts
|
|
495
|
+
var walnutCreekTimes = definePublication({
|
|
496
|
+
slug: "walnutcreektimes",
|
|
497
|
+
name: "Walnut Creek Times",
|
|
498
|
+
masthead: ["Walnut", "Creek", "Times"],
|
|
499
|
+
region: "Walnut Creek",
|
|
500
|
+
regionLabel: "Walnut Creek",
|
|
501
|
+
towns: ["Walnut Creek", "Rossmoor", "Saranap", "Pleasant Hill"],
|
|
502
|
+
nearbyTowns: ["Martinez", "Concord", "Contra Costa"],
|
|
503
|
+
sections: ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health"],
|
|
504
|
+
description: "Independent local journalism covering civic decisions, downtown commerce, neighborhood life, and community achievements across Walnut Creek, Rossmoor, Saranap, and Pleasant Hill.",
|
|
505
|
+
tagline: "Local stories. Stronger communities.",
|
|
506
|
+
layout: "broadsheet",
|
|
507
|
+
layouts: ["broadsheet"],
|
|
508
|
+
features: { smile: true, jobs: true, classifieds: true },
|
|
509
|
+
modules: { home: ["daily-smile"] }
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// src/cache.ts
|
|
513
|
+
var NEWS_CACHE = {
|
|
514
|
+
feedRevalidateSeconds: 30,
|
|
515
|
+
articleRevalidateSeconds: 60,
|
|
516
|
+
edgeMaxAgeSeconds: 60,
|
|
517
|
+
staleWhileRevalidateSeconds: 300,
|
|
518
|
+
feedTag: "news-feed",
|
|
519
|
+
articleTag: "news-article",
|
|
520
|
+
inventoryTag: "public-news-discovery-v2",
|
|
521
|
+
paths: ["/", "/news"]
|
|
522
|
+
};
|
|
523
|
+
var feedCacheOptions = {
|
|
524
|
+
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
525
|
+
tags: [NEWS_CACHE.feedTag]
|
|
526
|
+
};
|
|
527
|
+
var articleCacheOptions = {
|
|
528
|
+
revalidate: NEWS_CACHE.articleRevalidateSeconds,
|
|
529
|
+
tags: [NEWS_CACHE.articleTag]
|
|
530
|
+
};
|
|
531
|
+
var inventoryCacheOptions = {
|
|
532
|
+
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
533
|
+
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.inventoryTag]
|
|
534
|
+
};
|
|
535
|
+
function edgeCacheControl() {
|
|
536
|
+
return `public, s-maxage=${NEWS_CACHE.edgeMaxAgeSeconds}, stale-while-revalidate=${NEWS_CACHE.staleWhileRevalidateSeconds}`;
|
|
537
|
+
}
|
|
538
|
+
function feedCacheKey(publicationId, before, section) {
|
|
539
|
+
return `${publicationId}:${before ?? ""}:${section ?? ""}`;
|
|
540
|
+
}
|
|
541
|
+
function articleCacheKey(publicationId, slug) {
|
|
542
|
+
return `${publicationId}:${slug}`;
|
|
543
|
+
}
|
|
544
|
+
var NewsError = class extends Error {
|
|
545
|
+
constructor(status, message) {
|
|
546
|
+
super(message);
|
|
547
|
+
this.status = status;
|
|
548
|
+
this.name = "NewsError";
|
|
549
|
+
}
|
|
550
|
+
status;
|
|
551
|
+
};
|
|
552
|
+
function shouldServeLastKnown(error) {
|
|
553
|
+
const status = error instanceof NewsError ? error.status : error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
554
|
+
return status !== 404;
|
|
555
|
+
}
|
|
556
|
+
async function withLastKnown(store, key, load, accept = () => true) {
|
|
557
|
+
try {
|
|
558
|
+
const value = await load();
|
|
559
|
+
if (accept(value)) store.set(key, value);
|
|
560
|
+
return value;
|
|
561
|
+
} catch (error) {
|
|
562
|
+
if (!shouldServeLastKnown(error)) throw error;
|
|
563
|
+
const cached = store.get(key);
|
|
564
|
+
if (cached) return cached;
|
|
565
|
+
throw error;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function secretsMatch(provided, secret) {
|
|
569
|
+
if (!provided || !secret) return false;
|
|
570
|
+
const left = Array.from(provided);
|
|
571
|
+
const right = Array.from(secret);
|
|
572
|
+
let mismatch = left.length === right.length ? 0 : 1;
|
|
573
|
+
const length = Math.max(left.length, right.length);
|
|
574
|
+
for (let index = 0; index < length; index += 1) {
|
|
575
|
+
mismatch |= (left[index]?.codePointAt(0) ?? 0) ^ (right[index]?.codePointAt(0) ?? 0);
|
|
576
|
+
}
|
|
577
|
+
return mismatch === 0;
|
|
578
|
+
}
|
|
579
|
+
function revalidateDecision(input) {
|
|
580
|
+
if (!secretsMatch(input.providedSecret, input.configuredSecret)) {
|
|
581
|
+
return { status: 401, body: { error: "Unauthorized" } };
|
|
582
|
+
}
|
|
583
|
+
const tag = input.tag?.trim() || `${NEWS_CACHE.feedTag}, ${NEWS_CACHE.articleTag}`;
|
|
584
|
+
const path = input.path?.trim() || NEWS_CACHE.paths.join(", ");
|
|
585
|
+
return { status: 200, body: { revalidated: true, tag, path, now: input.now ?? Date.now() } };
|
|
586
|
+
}
|
|
587
|
+
function defaultRevalidateTargets() {
|
|
588
|
+
return {
|
|
589
|
+
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.articleTag, NEWS_CACHE.inventoryTag],
|
|
590
|
+
paths: [...NEWS_CACHE.paths]
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
435
594
|
// src/analytics.ts
|
|
436
595
|
var ANALYTICS_VERSION = 1;
|
|
437
596
|
var ANALYTICS_COOKIE = "napsite_analytics_v1";
|
|
@@ -3174,6 +3333,7 @@ exports.EVENTS = EVENTS;
|
|
|
3174
3333
|
exports.FEATURE_NAMES = FEATURE_NAMES;
|
|
3175
3334
|
exports.JOB_EMPLOYMENT_TYPES = JOB_EMPLOYMENT_TYPES;
|
|
3176
3335
|
exports.LAYOUT_COOKIE = LAYOUT_COOKIE;
|
|
3336
|
+
exports.LOCAL_BRIEFING_POLICY_VERSION = LOCAL_BRIEFING_POLICY_VERSION;
|
|
3177
3337
|
exports.LocalNetworkBar = LocalNetworkBar;
|
|
3178
3338
|
exports.MEET_ACTIVITIES = MEET_ACTIVITIES;
|
|
3179
3339
|
exports.NETWORK_PARENT = NETWORK_PARENT;
|
|
@@ -3210,6 +3370,7 @@ exports.clipChars = clipChars;
|
|
|
3210
3370
|
exports.communityMapEmbeds = communityMapEmbeds;
|
|
3211
3371
|
exports.communityPublications = communityPublications;
|
|
3212
3372
|
exports.composeEdition = composeEdition;
|
|
3373
|
+
exports.composeLocalBriefing = composeLocalBriefing;
|
|
3213
3374
|
exports.dealDollars = dealDollars;
|
|
3214
3375
|
exports.dealGoPath = dealGoPath;
|
|
3215
3376
|
exports.dealsFeedPath = dealsFeedPath;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as PublicationTheme, P as PublicationInput, N as NewsPublication, F as FeatureName,
|
|
2
|
-
export { A as ArticleCitation, e as CardType, f as CategoryRef, g as CouponSettings, h as CoverImageMetadata, i as FEATURE_NAMES, j as FeatureFlags, k as NewsFeed, l as NewsLayoutId, m as PublicationModules, R as ReferralSettings, S as StoryDisplay, T as TagRef, V as VerifiedCard, n as VerifiedCardAction, o as VerifiedCardFact, p as VerifiedCardImage } from './types-
|
|
1
|
+
import { a as PublicationTheme, P as PublicationInput, N as NewsPublication, F as FeatureName, b as NewsItem, H as HomepageSelection, c as NearbyUpdate, E as EditorialSection, d as NewsArticle, C as CouponOffer } from './types-BJFCWWi0.cjs';
|
|
2
|
+
export { A as ArticleCitation, e as CardType, f as CategoryRef, g as CouponSettings, h as CoverImageMetadata, i as FEATURE_NAMES, j as FeatureFlags, k as NewsFeed, l as NewsLayoutId, m as PublicationModules, R as ReferralSettings, S as StoryDisplay, T as TagRef, V as VerifiedCard, n as VerifiedCardAction, o as VerifiedCardFact, p as VerifiedCardImage } from './types-BJFCWWi0.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
|
-
export { b as PublicationRecord, P as PublicationSnapshot, S as SnapshotHooks, c as SyncSource, a as SyncedPublication, d as acceptRemoteRecord, e as acceptSnapshot, p as parseSnapshot, f as publicationFromSnapshot, g as publicationRecord, s as snapshotFromRecord, h as syncPublication } from './snapshot-
|
|
5
|
+
export { b as PublicationRecord, P as PublicationSnapshot, S as SnapshotHooks, c as SyncSource, a as SyncedPublication, d as acceptRemoteRecord, e as acceptSnapshot, p as parseSnapshot, f as publicationFromSnapshot, g as publicationRecord, s as snapshotFromRecord, h as syncPublication } from './snapshot-Dx4Lv-lB.cjs';
|
|
6
6
|
import { CookieCategoryDefinition, CookieConsentRecord } from '@scalemule/compliance';
|
|
7
7
|
export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, CookieCategory, CookieCategoryDefinition, CookieConsentRecord, CookieDisclosure, CookiePreferences, 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';
|
|
8
8
|
export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.cjs';
|
|
@@ -32,6 +32,112 @@ declare const LAYOUT_COOKIE = "news_publication_layout";
|
|
|
32
32
|
*/
|
|
33
33
|
declare function resolveLayout(publication: NewsPublication, cookieValue?: string | null): string;
|
|
34
34
|
|
|
35
|
+
declare const LOCAL_BRIEFING_POLICY_VERSION = "1";
|
|
36
|
+
type BriefingVerification = "unverified" | "community_submitted" | "officially_confirmed" | "independently_verified" | "disputed";
|
|
37
|
+
type BriefingSection = "important" | "around_you" | "people" | "discover" | "participate" | "following";
|
|
38
|
+
/** Public evidence links only. Artifact URLs and private reporter notes do not belong here. */
|
|
39
|
+
interface BriefingSource {
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
title: string;
|
|
43
|
+
publisher: string;
|
|
44
|
+
}
|
|
45
|
+
/** Explicit choices only; no inferred demographics, relationships or reading history. */
|
|
46
|
+
interface BriefingPreferences {
|
|
47
|
+
community_ids?: readonly string[];
|
|
48
|
+
topic_ids?: readonly string[];
|
|
49
|
+
entity_ids?: readonly string[];
|
|
50
|
+
issue_ids?: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A server-authorized projection, not a new canonical record or submission API.
|
|
54
|
+
* Article-backed presentation only: BaseNews records need not have an article.
|
|
55
|
+
* Eligibility here must never gate existing publication or community workflows.
|
|
56
|
+
* The producer must resolve grants, evidence rights and geographic relationships.
|
|
57
|
+
* Keep withdrawn/superseded revisions in the input to suppress older copies.
|
|
58
|
+
*/
|
|
59
|
+
interface BriefingCandidate {
|
|
60
|
+
sm_application_id: string;
|
|
61
|
+
publication_id: string;
|
|
62
|
+
/** Namespaced existing development/occurrence/reporting ID, never an issue-wide ID. */
|
|
63
|
+
canonical_key: string;
|
|
64
|
+
revision: number;
|
|
65
|
+
article: NewsItem;
|
|
66
|
+
publication_status: "published" | "draft";
|
|
67
|
+
visibility: "public" | "private";
|
|
68
|
+
editorial_approval: "approved" | "pending";
|
|
69
|
+
lifecycle: "current" | "superseded" | "withdrawn";
|
|
70
|
+
/** Resolved public area IDs, including applicable containing areas. No coordinates. */
|
|
71
|
+
geography_ids: readonly string[];
|
|
72
|
+
topic_ids: readonly string[];
|
|
73
|
+
entity_ids: readonly string[];
|
|
74
|
+
issue_ids: readonly string[];
|
|
75
|
+
section: BriefingSection;
|
|
76
|
+
/** Editorial assignments, never inferred from emotional language or click rates. */
|
|
77
|
+
civic_importance: "routine" | "important";
|
|
78
|
+
practical_value: boolean;
|
|
79
|
+
discovery_value: boolean;
|
|
80
|
+
why_it_matters?: string;
|
|
81
|
+
/** Required editorial relevance expiry; article publication time is not event time. */
|
|
82
|
+
relevant_until: string;
|
|
83
|
+
verification: BriefingVerification;
|
|
84
|
+
last_verified_at: string | null;
|
|
85
|
+
review_due_at: string | null;
|
|
86
|
+
sources: readonly BriefingSource[];
|
|
87
|
+
}
|
|
88
|
+
type BriefingReason = "important_civic_information" | "selected_community" | "publication_coverage" | "practical_value" | "following_entity" | "following_issue" | "selected_topic" | "recent_reporting" | "local_discovery";
|
|
89
|
+
interface LocalBriefingOptions {
|
|
90
|
+
sm_application_id: string;
|
|
91
|
+
publication_id: string;
|
|
92
|
+
coverage_area_ids: readonly string[];
|
|
93
|
+
preferences?: BriefingPreferences;
|
|
94
|
+
personalized?: boolean;
|
|
95
|
+
/** Maximum, never a quota. Between 1 and 12, default 6. */
|
|
96
|
+
limit?: number;
|
|
97
|
+
now?: number;
|
|
98
|
+
}
|
|
99
|
+
interface LocalBriefingItem {
|
|
100
|
+
canonical_key: string;
|
|
101
|
+
revision: number;
|
|
102
|
+
article: Pick<NewsItem, "id" | "slug" | "title" | "published_at" | "author_name" | "canonical_url">;
|
|
103
|
+
summary: string;
|
|
104
|
+
section: BriefingSection;
|
|
105
|
+
geography_ids: string[];
|
|
106
|
+
why_it_matters?: string;
|
|
107
|
+
reasons: BriefingReason[];
|
|
108
|
+
relevance: {
|
|
109
|
+
geographic: number;
|
|
110
|
+
practical: number;
|
|
111
|
+
relationship: number;
|
|
112
|
+
interest: number;
|
|
113
|
+
temporal: number;
|
|
114
|
+
civic: number;
|
|
115
|
+
discovery: number;
|
|
116
|
+
};
|
|
117
|
+
verification: BriefingVerification;
|
|
118
|
+
freshness: "current" | "unknown";
|
|
119
|
+
last_verified_at: string | null;
|
|
120
|
+
review_due_at: string | null;
|
|
121
|
+
relevant_until: string;
|
|
122
|
+
sources: BriefingSource[];
|
|
123
|
+
}
|
|
124
|
+
interface LocalBriefing {
|
|
125
|
+
schema_version: 1;
|
|
126
|
+
policy_version: typeof LOCAL_BRIEFING_POLICY_VERSION;
|
|
127
|
+
publication_id: string;
|
|
128
|
+
generated_at: string;
|
|
129
|
+
personalized: boolean;
|
|
130
|
+
items: LocalBriefingItem[];
|
|
131
|
+
/** Consumers must keep a visible route to important coverage beyond the brief. */
|
|
132
|
+
remaining_important_count: number;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Opt-in, deterministic presentation of existing public records. This does not
|
|
136
|
+
* authenticate callers, fetch evidence, verify claims, persist preferences or
|
|
137
|
+
* publish anything. Never pass raw user submissions as authorized candidates.
|
|
138
|
+
*/
|
|
139
|
+
declare function composeLocalBriefing(candidates: readonly BriefingCandidate[], options: LocalBriefingOptions): LocalBriefing;
|
|
140
|
+
|
|
35
141
|
/**
|
|
36
142
|
* Walnut Creek Times, the reference paper.
|
|
37
143
|
* Other papers call `definePublication` with their own name, towns, and flags.
|
|
@@ -367,4 +473,4 @@ declare function publicationListeningCandidates<T extends ListeningArticle>(item
|
|
|
367
473
|
*/
|
|
368
474
|
declare const CURATED_COMMUNITY_CARDS: Record<string, Record<string, unknown>[]>;
|
|
369
475
|
|
|
370
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, type ListeningArticle, 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, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
476
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, 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, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, 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,8 +1,8 @@
|
|
|
1
|
-
import { a as PublicationTheme, P as PublicationInput, N as NewsPublication, F as FeatureName,
|
|
2
|
-
export { A as ArticleCitation, e as CardType, f as CategoryRef, g as CouponSettings, h as CoverImageMetadata, i as FEATURE_NAMES, j as FeatureFlags, k as NewsFeed, l as NewsLayoutId, m as PublicationModules, R as ReferralSettings, S as StoryDisplay, T as TagRef, V as VerifiedCard, n as VerifiedCardAction, o as VerifiedCardFact, p as VerifiedCardImage } from './types-
|
|
1
|
+
import { a as PublicationTheme, P as PublicationInput, N as NewsPublication, F as FeatureName, b as NewsItem, H as HomepageSelection, c as NearbyUpdate, E as EditorialSection, d as NewsArticle, C as CouponOffer } from './types-BJFCWWi0.js';
|
|
2
|
+
export { A as ArticleCitation, e as CardType, f as CategoryRef, g as CouponSettings, h as CoverImageMetadata, i as FEATURE_NAMES, j as FeatureFlags, k as NewsFeed, l as NewsLayoutId, m as PublicationModules, R as ReferralSettings, S as StoryDisplay, T as TagRef, V as VerifiedCard, n as VerifiedCardAction, o as VerifiedCardFact, p as VerifiedCardImage } from './types-BJFCWWi0.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
|
-
export { b as PublicationRecord, P as PublicationSnapshot, S as SnapshotHooks, c as SyncSource, a as SyncedPublication, d as acceptRemoteRecord, e as acceptSnapshot, p as parseSnapshot, f as publicationFromSnapshot, g as publicationRecord, s as snapshotFromRecord, h as syncPublication } from './snapshot-
|
|
5
|
+
export { b as PublicationRecord, P as PublicationSnapshot, S as SnapshotHooks, c as SyncSource, a as SyncedPublication, d as acceptRemoteRecord, e as acceptSnapshot, p as parseSnapshot, f as publicationFromSnapshot, g as publicationRecord, s as snapshotFromRecord, h as syncPublication } from './snapshot-DmRw8G_B.js';
|
|
6
6
|
import { CookieCategoryDefinition, CookieConsentRecord } from '@scalemule/compliance';
|
|
7
7
|
export { COOKIE_CONSENT_BACKUP_KEY, COOKIE_CONSENT_EVENT, COOKIE_CONSENT_KEY, COOKIE_CONSENT_MAX_AGE_SECONDS, CookieCategory, CookieCategoryDefinition, CookieConsentRecord, CookieDisclosure, CookiePreferences, 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';
|
|
8
8
|
export { CookieConsentBanner, CookieConsentBannerProps } from './cookie-consent-banner.js';
|
|
@@ -32,6 +32,112 @@ declare const LAYOUT_COOKIE = "news_publication_layout";
|
|
|
32
32
|
*/
|
|
33
33
|
declare function resolveLayout(publication: NewsPublication, cookieValue?: string | null): string;
|
|
34
34
|
|
|
35
|
+
declare const LOCAL_BRIEFING_POLICY_VERSION = "1";
|
|
36
|
+
type BriefingVerification = "unverified" | "community_submitted" | "officially_confirmed" | "independently_verified" | "disputed";
|
|
37
|
+
type BriefingSection = "important" | "around_you" | "people" | "discover" | "participate" | "following";
|
|
38
|
+
/** Public evidence links only. Artifact URLs and private reporter notes do not belong here. */
|
|
39
|
+
interface BriefingSource {
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
title: string;
|
|
43
|
+
publisher: string;
|
|
44
|
+
}
|
|
45
|
+
/** Explicit choices only; no inferred demographics, relationships or reading history. */
|
|
46
|
+
interface BriefingPreferences {
|
|
47
|
+
community_ids?: readonly string[];
|
|
48
|
+
topic_ids?: readonly string[];
|
|
49
|
+
entity_ids?: readonly string[];
|
|
50
|
+
issue_ids?: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A server-authorized projection, not a new canonical record or submission API.
|
|
54
|
+
* Article-backed presentation only: BaseNews records need not have an article.
|
|
55
|
+
* Eligibility here must never gate existing publication or community workflows.
|
|
56
|
+
* The producer must resolve grants, evidence rights and geographic relationships.
|
|
57
|
+
* Keep withdrawn/superseded revisions in the input to suppress older copies.
|
|
58
|
+
*/
|
|
59
|
+
interface BriefingCandidate {
|
|
60
|
+
sm_application_id: string;
|
|
61
|
+
publication_id: string;
|
|
62
|
+
/** Namespaced existing development/occurrence/reporting ID, never an issue-wide ID. */
|
|
63
|
+
canonical_key: string;
|
|
64
|
+
revision: number;
|
|
65
|
+
article: NewsItem;
|
|
66
|
+
publication_status: "published" | "draft";
|
|
67
|
+
visibility: "public" | "private";
|
|
68
|
+
editorial_approval: "approved" | "pending";
|
|
69
|
+
lifecycle: "current" | "superseded" | "withdrawn";
|
|
70
|
+
/** Resolved public area IDs, including applicable containing areas. No coordinates. */
|
|
71
|
+
geography_ids: readonly string[];
|
|
72
|
+
topic_ids: readonly string[];
|
|
73
|
+
entity_ids: readonly string[];
|
|
74
|
+
issue_ids: readonly string[];
|
|
75
|
+
section: BriefingSection;
|
|
76
|
+
/** Editorial assignments, never inferred from emotional language or click rates. */
|
|
77
|
+
civic_importance: "routine" | "important";
|
|
78
|
+
practical_value: boolean;
|
|
79
|
+
discovery_value: boolean;
|
|
80
|
+
why_it_matters?: string;
|
|
81
|
+
/** Required editorial relevance expiry; article publication time is not event time. */
|
|
82
|
+
relevant_until: string;
|
|
83
|
+
verification: BriefingVerification;
|
|
84
|
+
last_verified_at: string | null;
|
|
85
|
+
review_due_at: string | null;
|
|
86
|
+
sources: readonly BriefingSource[];
|
|
87
|
+
}
|
|
88
|
+
type BriefingReason = "important_civic_information" | "selected_community" | "publication_coverage" | "practical_value" | "following_entity" | "following_issue" | "selected_topic" | "recent_reporting" | "local_discovery";
|
|
89
|
+
interface LocalBriefingOptions {
|
|
90
|
+
sm_application_id: string;
|
|
91
|
+
publication_id: string;
|
|
92
|
+
coverage_area_ids: readonly string[];
|
|
93
|
+
preferences?: BriefingPreferences;
|
|
94
|
+
personalized?: boolean;
|
|
95
|
+
/** Maximum, never a quota. Between 1 and 12, default 6. */
|
|
96
|
+
limit?: number;
|
|
97
|
+
now?: number;
|
|
98
|
+
}
|
|
99
|
+
interface LocalBriefingItem {
|
|
100
|
+
canonical_key: string;
|
|
101
|
+
revision: number;
|
|
102
|
+
article: Pick<NewsItem, "id" | "slug" | "title" | "published_at" | "author_name" | "canonical_url">;
|
|
103
|
+
summary: string;
|
|
104
|
+
section: BriefingSection;
|
|
105
|
+
geography_ids: string[];
|
|
106
|
+
why_it_matters?: string;
|
|
107
|
+
reasons: BriefingReason[];
|
|
108
|
+
relevance: {
|
|
109
|
+
geographic: number;
|
|
110
|
+
practical: number;
|
|
111
|
+
relationship: number;
|
|
112
|
+
interest: number;
|
|
113
|
+
temporal: number;
|
|
114
|
+
civic: number;
|
|
115
|
+
discovery: number;
|
|
116
|
+
};
|
|
117
|
+
verification: BriefingVerification;
|
|
118
|
+
freshness: "current" | "unknown";
|
|
119
|
+
last_verified_at: string | null;
|
|
120
|
+
review_due_at: string | null;
|
|
121
|
+
relevant_until: string;
|
|
122
|
+
sources: BriefingSource[];
|
|
123
|
+
}
|
|
124
|
+
interface LocalBriefing {
|
|
125
|
+
schema_version: 1;
|
|
126
|
+
policy_version: typeof LOCAL_BRIEFING_POLICY_VERSION;
|
|
127
|
+
publication_id: string;
|
|
128
|
+
generated_at: string;
|
|
129
|
+
personalized: boolean;
|
|
130
|
+
items: LocalBriefingItem[];
|
|
131
|
+
/** Consumers must keep a visible route to important coverage beyond the brief. */
|
|
132
|
+
remaining_important_count: number;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Opt-in, deterministic presentation of existing public records. This does not
|
|
136
|
+
* authenticate callers, fetch evidence, verify claims, persist preferences or
|
|
137
|
+
* publish anything. Never pass raw user submissions as authorized candidates.
|
|
138
|
+
*/
|
|
139
|
+
declare function composeLocalBriefing(candidates: readonly BriefingCandidate[], options: LocalBriefingOptions): LocalBriefing;
|
|
140
|
+
|
|
35
141
|
/**
|
|
36
142
|
* Walnut Creek Times, the reference paper.
|
|
37
143
|
* Other papers call `definePublication` with their own name, towns, and flags.
|
|
@@ -367,4 +473,4 @@ declare function publicationListeningCandidates<T extends ListeningArticle>(item
|
|
|
367
473
|
*/
|
|
368
474
|
declare const CURATED_COMMUNITY_CARDS: Record<string, Record<string, unknown>[]>;
|
|
369
475
|
|
|
370
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, type ListeningArticle, 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, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
476
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, type Attribution, BROADSHEET_THEME, type BriefingCandidate, type BriefingPreferences, type BriefingReason, type BriefingSection, type BriefingSource, type BriefingVerification, COOKIE_CATEGORIES, CURATED_COMMUNITY_CARDS, CouponOffer, EDITORIAL_SECTION_ORDER, EVENTS, EditorialSection, type EffectiveTheme, FeatureName, HomepageSelection, LAYOUT_COOKIE, LOCAL_BRIEFING_POLICY_VERSION, type ListeningArticle, type LocalBriefing, type LocalBriefingItem, type LocalBriefingOptions, 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, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, 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
|
@@ -14,106 +14,6 @@ export { CLASSIFIED_CATEGORIES, JOB_EMPLOYMENT_TYPES, categoryLabel, classifiedP
|
|
|
14
14
|
export { MEET_ACTIVITIES, activityLabel, canHostMeetup, isAdult, prepareMeetup, screenMessage } from './chunk-GB36YM56.js';
|
|
15
15
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
16
16
|
|
|
17
|
-
// src/preset.ts
|
|
18
|
-
var walnutCreekTimes = definePublication({
|
|
19
|
-
slug: "walnutcreektimes",
|
|
20
|
-
name: "Walnut Creek Times",
|
|
21
|
-
masthead: ["Walnut", "Creek", "Times"],
|
|
22
|
-
region: "Walnut Creek",
|
|
23
|
-
regionLabel: "Walnut Creek",
|
|
24
|
-
towns: ["Walnut Creek", "Rossmoor", "Saranap", "Pleasant Hill"],
|
|
25
|
-
nearbyTowns: ["Martinez", "Concord", "Contra Costa"],
|
|
26
|
-
sections: ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health"],
|
|
27
|
-
description: "Independent local journalism covering civic decisions, downtown commerce, neighborhood life, and community achievements across Walnut Creek, Rossmoor, Saranap, and Pleasant Hill.",
|
|
28
|
-
tagline: "Local stories. Stronger communities.",
|
|
29
|
-
layout: "broadsheet",
|
|
30
|
-
layouts: ["broadsheet"],
|
|
31
|
-
features: { smile: true, jobs: true, classifieds: true },
|
|
32
|
-
modules: { home: ["daily-smile"] }
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// src/cache.ts
|
|
36
|
-
var NEWS_CACHE = {
|
|
37
|
-
feedRevalidateSeconds: 30,
|
|
38
|
-
articleRevalidateSeconds: 60,
|
|
39
|
-
edgeMaxAgeSeconds: 60,
|
|
40
|
-
staleWhileRevalidateSeconds: 300,
|
|
41
|
-
feedTag: "news-feed",
|
|
42
|
-
articleTag: "news-article",
|
|
43
|
-
inventoryTag: "public-news-discovery-v2",
|
|
44
|
-
paths: ["/", "/news"]
|
|
45
|
-
};
|
|
46
|
-
var feedCacheOptions = {
|
|
47
|
-
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
48
|
-
tags: [NEWS_CACHE.feedTag]
|
|
49
|
-
};
|
|
50
|
-
var articleCacheOptions = {
|
|
51
|
-
revalidate: NEWS_CACHE.articleRevalidateSeconds,
|
|
52
|
-
tags: [NEWS_CACHE.articleTag]
|
|
53
|
-
};
|
|
54
|
-
var inventoryCacheOptions = {
|
|
55
|
-
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
56
|
-
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.inventoryTag]
|
|
57
|
-
};
|
|
58
|
-
function edgeCacheControl() {
|
|
59
|
-
return `public, s-maxage=${NEWS_CACHE.edgeMaxAgeSeconds}, stale-while-revalidate=${NEWS_CACHE.staleWhileRevalidateSeconds}`;
|
|
60
|
-
}
|
|
61
|
-
function feedCacheKey(publicationId, before, section) {
|
|
62
|
-
return `${publicationId}:${before ?? ""}:${section ?? ""}`;
|
|
63
|
-
}
|
|
64
|
-
function articleCacheKey(publicationId, slug) {
|
|
65
|
-
return `${publicationId}:${slug}`;
|
|
66
|
-
}
|
|
67
|
-
var NewsError = class extends Error {
|
|
68
|
-
constructor(status, message) {
|
|
69
|
-
super(message);
|
|
70
|
-
this.status = status;
|
|
71
|
-
this.name = "NewsError";
|
|
72
|
-
}
|
|
73
|
-
status;
|
|
74
|
-
};
|
|
75
|
-
function shouldServeLastKnown(error) {
|
|
76
|
-
const status = error instanceof NewsError ? error.status : error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
77
|
-
return status !== 404;
|
|
78
|
-
}
|
|
79
|
-
async function withLastKnown(store, key, load, accept = () => true) {
|
|
80
|
-
try {
|
|
81
|
-
const value = await load();
|
|
82
|
-
if (accept(value)) store.set(key, value);
|
|
83
|
-
return value;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
if (!shouldServeLastKnown(error)) throw error;
|
|
86
|
-
const cached = store.get(key);
|
|
87
|
-
if (cached) return cached;
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function secretsMatch(provided, secret) {
|
|
92
|
-
if (!provided || !secret) return false;
|
|
93
|
-
const left = Array.from(provided);
|
|
94
|
-
const right = Array.from(secret);
|
|
95
|
-
let mismatch = left.length === right.length ? 0 : 1;
|
|
96
|
-
const length = Math.max(left.length, right.length);
|
|
97
|
-
for (let index = 0; index < length; index += 1) {
|
|
98
|
-
mismatch |= (left[index]?.codePointAt(0) ?? 0) ^ (right[index]?.codePointAt(0) ?? 0);
|
|
99
|
-
}
|
|
100
|
-
return mismatch === 0;
|
|
101
|
-
}
|
|
102
|
-
function revalidateDecision(input) {
|
|
103
|
-
if (!secretsMatch(input.providedSecret, input.configuredSecret)) {
|
|
104
|
-
return { status: 401, body: { error: "Unauthorized" } };
|
|
105
|
-
}
|
|
106
|
-
const tag = input.tag?.trim() || `${NEWS_CACHE.feedTag}, ${NEWS_CACHE.articleTag}`;
|
|
107
|
-
const path = input.path?.trim() || NEWS_CACHE.paths.join(", ");
|
|
108
|
-
return { status: 200, body: { revalidated: true, tag, path, now: input.now ?? Date.now() } };
|
|
109
|
-
}
|
|
110
|
-
function defaultRevalidateTargets() {
|
|
111
|
-
return {
|
|
112
|
-
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.articleTag, NEWS_CACHE.inventoryTag],
|
|
113
|
-
paths: [...NEWS_CACHE.paths]
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
17
|
// src/editorial.ts
|
|
118
18
|
var labels = {
|
|
119
19
|
"Local businesses": "Business",
|
|
@@ -131,8 +31,8 @@ var EDITORIAL_SECTION_ORDER = [
|
|
|
131
31
|
"Schools",
|
|
132
32
|
"Opinion"
|
|
133
33
|
];
|
|
134
|
-
function orderedSections(
|
|
135
|
-
return
|
|
34
|
+
function orderedSections(sections2, order = EDITORIAL_SECTION_ORDER) {
|
|
35
|
+
return sections2.filter((section) => section.name !== "Local news").slice().sort((left, right) => {
|
|
136
36
|
const leftRank = order.indexOf(left.name);
|
|
137
37
|
const rightRank = order.indexOf(right.name);
|
|
138
38
|
const leftOrder = leftRank === -1 ? order.length : leftRank;
|
|
@@ -193,12 +93,12 @@ function composeEdition(items, selection = {}, now = Date.now()) {
|
|
|
193
93
|
secondary.push(deferredWithDuplicateImage.shift());
|
|
194
94
|
}
|
|
195
95
|
for (const item of secondary) remaining.splice(remaining.indexOf(item), 1);
|
|
196
|
-
const
|
|
96
|
+
const eligible2 = remaining.filter((item) => {
|
|
197
97
|
const age = now - Date.parse(item.published_at);
|
|
198
98
|
return item.excerpt?.trim() && age >= 0 && age <= 7 * 864e5;
|
|
199
99
|
});
|
|
200
|
-
const selected = (pins.brief_slugs ?? []).flatMap((slug) =>
|
|
201
|
-
const candidates = [...selected, ...editorialSections(
|
|
100
|
+
const selected = (pins.brief_slugs ?? []).flatMap((slug) => eligible2.find((item) => item.slug === slug) ?? []);
|
|
101
|
+
const candidates = [...selected, ...editorialSections(eligible2).map((group) => group.items[0]), ...eligible2];
|
|
202
102
|
const briefIds = /* @__PURE__ */ new Set();
|
|
203
103
|
const briefs = candidates.filter((item) => {
|
|
204
104
|
if (!item || briefIds.has(item.id) || briefIds.size >= 4) return false;
|
|
@@ -207,8 +107,8 @@ function composeEdition(items, selection = {}, now = Date.now()) {
|
|
|
207
107
|
});
|
|
208
108
|
const compact = [...briefs, ...remaining.filter((item) => !briefIds.has(item.id))].slice(0, 4);
|
|
209
109
|
const compactIds = new Set(compact.map((item) => item.id));
|
|
210
|
-
const
|
|
211
|
-
return { lead, secondary, briefs, compact, sections, navigation: editorialSections(inventory) };
|
|
110
|
+
const sections2 = editorialSections(remaining.filter((item) => !compactIds.has(item.id)));
|
|
111
|
+
return { lead, secondary, briefs, compact, sections: sections2, navigation: editorialSections(inventory) };
|
|
212
112
|
}
|
|
213
113
|
function displayHeadline(article, selection = {}) {
|
|
214
114
|
return selection.stories?.[article.slug]?.display_title?.trim() || article.title;
|
|
@@ -373,6 +273,265 @@ function llmsText(publication, canonical) {
|
|
|
373
273
|
].join("\n");
|
|
374
274
|
}
|
|
375
275
|
|
|
276
|
+
// src/briefing.ts
|
|
277
|
+
var LOCAL_BRIEFING_POLICY_VERSION = "1";
|
|
278
|
+
var DAY = 864e5;
|
|
279
|
+
var verifications = /* @__PURE__ */ new Set([
|
|
280
|
+
"unverified",
|
|
281
|
+
"community_submitted",
|
|
282
|
+
"officially_confirmed",
|
|
283
|
+
"independently_verified",
|
|
284
|
+
"disputed"
|
|
285
|
+
]);
|
|
286
|
+
var sections = /* @__PURE__ */ new Set(["important", "around_you", "people", "discover", "participate", "following"]);
|
|
287
|
+
var overlaps = (a, b = []) => a.some((id) => id.trim() !== "" && b.includes(id));
|
|
288
|
+
function timestamp(value) {
|
|
289
|
+
if (!/^\d{4}-\d{2}-\d{2}T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,3})?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.test(value) || !validCalendarDate(value.slice(0, 10))) return NaN;
|
|
290
|
+
return Date.parse(value);
|
|
291
|
+
}
|
|
292
|
+
function sourcesFor(candidate) {
|
|
293
|
+
const seen = /* @__PURE__ */ new Set();
|
|
294
|
+
return candidate.sources.flatMap((source) => {
|
|
295
|
+
const url = publicHttps(source.url);
|
|
296
|
+
if (!source.id.trim() || !source.publisher.trim() || !source.title.trim() || !url || seen.has(source.id)) return [];
|
|
297
|
+
seen.add(source.id);
|
|
298
|
+
return [{ id: source.id, url, title: source.title, publisher: source.publisher }];
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function eligible(candidate, coverage, now) {
|
|
302
|
+
if (candidate.publication_status !== "published" || candidate.visibility !== "public" || candidate.editorial_approval !== "approved" || candidate.lifecycle !== "current" || !candidate.article.id.trim() || !candidate.article.slug.trim() || !candidate.article.title.trim() || !verifications.has(candidate.verification) || !sections.has(candidate.section) || !overlaps(candidate.geography_ids, coverage)) return false;
|
|
303
|
+
const published = timestamp(candidate.article.published_at);
|
|
304
|
+
const expiry = timestamp(candidate.relevant_until);
|
|
305
|
+
if (!Number.isFinite(published) || published > now || !Number.isFinite(expiry) || expiry <= now) return false;
|
|
306
|
+
const verified = candidate.last_verified_at === null ? null : timestamp(candidate.last_verified_at);
|
|
307
|
+
const review = candidate.review_due_at === null ? null : timestamp(candidate.review_due_at);
|
|
308
|
+
if (verified !== null && (!Number.isFinite(verified) || verified > now)) return false;
|
|
309
|
+
if (review !== null && (!Number.isFinite(review) || review <= now || verified !== null && review <= verified)) return false;
|
|
310
|
+
const confirmed = candidate.verification === "officially_confirmed" || candidate.verification === "independently_verified";
|
|
311
|
+
return !confirmed || verified !== null && review !== null && sourcesFor(candidate).length > 0;
|
|
312
|
+
}
|
|
313
|
+
function projectionKey(candidate) {
|
|
314
|
+
const article = candidate.article;
|
|
315
|
+
return JSON.stringify([
|
|
316
|
+
article.id,
|
|
317
|
+
article.slug,
|
|
318
|
+
article.title,
|
|
319
|
+
article.excerpt,
|
|
320
|
+
article.published_at,
|
|
321
|
+
article.author_name,
|
|
322
|
+
publicHttps(article.canonical_url),
|
|
323
|
+
[...candidate.geography_ids].sort(),
|
|
324
|
+
[...candidate.topic_ids].sort(),
|
|
325
|
+
[...candidate.entity_ids].sort(),
|
|
326
|
+
[...candidate.issue_ids].sort(),
|
|
327
|
+
candidate.section,
|
|
328
|
+
candidate.civic_importance,
|
|
329
|
+
candidate.practical_value,
|
|
330
|
+
candidate.discovery_value,
|
|
331
|
+
candidate.why_it_matters,
|
|
332
|
+
candidate.relevant_until,
|
|
333
|
+
candidate.verification,
|
|
334
|
+
candidate.last_verified_at,
|
|
335
|
+
candidate.review_due_at,
|
|
336
|
+
sourcesFor(candidate).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
|
|
337
|
+
]);
|
|
338
|
+
}
|
|
339
|
+
function composeLocalBriefing(candidates, options) {
|
|
340
|
+
const now = options.now ?? Date.now();
|
|
341
|
+
const limit = options.limit ?? 6;
|
|
342
|
+
if (!options.sm_application_id.trim() || !options.publication_id.trim() || !Number.isFinite(now) || !Number.isFinite(new Date(now).getTime()) || !Number.isInteger(limit) || limit < 1 || limit > 12) {
|
|
343
|
+
throw new RangeError("A briefing requires application/publication scope, a valid time and a limit from 1 to 12");
|
|
344
|
+
}
|
|
345
|
+
const preferences = options.personalized === false ? {} : options.preferences ?? {};
|
|
346
|
+
const personalized = [preferences.community_ids, preferences.topic_ids, preferences.entity_ids, preferences.issue_ids].some((ids) => ids?.some((id) => id.trim() !== ""));
|
|
347
|
+
const newest = /* @__PURE__ */ new Map();
|
|
348
|
+
for (const candidate of candidates) {
|
|
349
|
+
if (candidate.sm_application_id !== options.sm_application_id || candidate.publication_id !== options.publication_id || !candidate.canonical_key.trim() || !Number.isSafeInteger(candidate.revision) || candidate.revision < 1) continue;
|
|
350
|
+
const group = newest.get(candidate.canonical_key);
|
|
351
|
+
if (!group || candidate.revision > group[0].revision) newest.set(candidate.canonical_key, [candidate]);
|
|
352
|
+
else if (candidate.revision === group[0].revision) group.push(candidate);
|
|
353
|
+
}
|
|
354
|
+
const ranked = [];
|
|
355
|
+
for (const group of newest.values()) {
|
|
356
|
+
if (group.some((candidate2) => !eligible(candidate2, options.coverage_area_ids, now))) continue;
|
|
357
|
+
const candidate = group[0];
|
|
358
|
+
if (group.length > 1 && group.some((copy) => projectionKey(copy) !== projectionKey(candidate))) continue;
|
|
359
|
+
const summary = candidate.article.excerpt?.trim() ?? "";
|
|
360
|
+
if (!summary || Array.from(summary).length > 280 || !/[.!?。!?][”’"']?$/u.test(summary)) continue;
|
|
361
|
+
const community = overlaps(candidate.geography_ids, preferences.community_ids);
|
|
362
|
+
const entity = overlaps(candidate.entity_ids, preferences.entity_ids);
|
|
363
|
+
const issue = overlaps(candidate.issue_ids, preferences.issue_ids);
|
|
364
|
+
const topic = overlaps(candidate.topic_ids, preferences.topic_ids);
|
|
365
|
+
const published = timestamp(candidate.article.published_at);
|
|
366
|
+
const age = now - published;
|
|
367
|
+
const important = candidate.civic_importance === "important";
|
|
368
|
+
const relevance = {
|
|
369
|
+
geographic: community ? 2 : 1,
|
|
370
|
+
practical: candidate.practical_value ? 1 : 0,
|
|
371
|
+
relationship: entity || issue ? 1 : 0,
|
|
372
|
+
interest: topic ? 1 : 0,
|
|
373
|
+
temporal: age < DAY ? 2 : age < 7 * DAY ? 1 : 0,
|
|
374
|
+
civic: important ? 1 : 0,
|
|
375
|
+
discovery: candidate.discovery_value ? 1 : 0
|
|
376
|
+
};
|
|
377
|
+
const reasons = [];
|
|
378
|
+
if (important) reasons.push("important_civic_information");
|
|
379
|
+
reasons.push(community ? "selected_community" : "publication_coverage");
|
|
380
|
+
if (candidate.practical_value) reasons.push("practical_value");
|
|
381
|
+
if (entity) reasons.push("following_entity");
|
|
382
|
+
if (issue) reasons.push("following_issue");
|
|
383
|
+
if (topic) reasons.push("selected_topic");
|
|
384
|
+
if (relevance.temporal > 0) reasons.push("recent_reporting");
|
|
385
|
+
if (candidate.discovery_value) reasons.push("local_discovery");
|
|
386
|
+
const article = candidate.article;
|
|
387
|
+
const item = {
|
|
388
|
+
canonical_key: candidate.canonical_key,
|
|
389
|
+
revision: candidate.revision,
|
|
390
|
+
article: {
|
|
391
|
+
id: article.id,
|
|
392
|
+
slug: article.slug,
|
|
393
|
+
title: article.title,
|
|
394
|
+
published_at: article.published_at,
|
|
395
|
+
author_name: article.author_name,
|
|
396
|
+
canonical_url: publicHttps(article.canonical_url) ?? null
|
|
397
|
+
},
|
|
398
|
+
summary,
|
|
399
|
+
section: important ? "important" : entity || issue ? "following" : candidate.section,
|
|
400
|
+
geography_ids: [...new Set(candidate.geography_ids)],
|
|
401
|
+
why_it_matters: candidate.why_it_matters?.trim() || void 0,
|
|
402
|
+
reasons,
|
|
403
|
+
relevance,
|
|
404
|
+
verification: candidate.verification,
|
|
405
|
+
freshness: candidate.last_verified_at !== null && candidate.review_due_at !== null ? "current" : "unknown",
|
|
406
|
+
last_verified_at: candidate.last_verified_at,
|
|
407
|
+
review_due_at: candidate.review_due_at,
|
|
408
|
+
relevant_until: candidate.relevant_until,
|
|
409
|
+
sources: sourcesFor(candidate)
|
|
410
|
+
};
|
|
411
|
+
const score = 4 * relevance.geographic + 3 * relevance.practical + 3 * relevance.relationship + 2 * relevance.interest + relevance.temporal + relevance.discovery;
|
|
412
|
+
ranked.push({ item, score, published, important });
|
|
413
|
+
}
|
|
414
|
+
ranked.sort((a, b) => Number(b.important) - Number(a.important) || b.score - a.score || b.published - a.published || (a.item.canonical_key < b.item.canonical_key ? -1 : a.item.canonical_key > b.item.canonical_key ? 1 : 0));
|
|
415
|
+
const seenArticles = /* @__PURE__ */ new Set();
|
|
416
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
417
|
+
const deduplicated = ranked.filter(({ item }) => {
|
|
418
|
+
const url = item.article.canonical_url;
|
|
419
|
+
if (seenArticles.has(item.article.id) || url && seenUrls.has(url)) return false;
|
|
420
|
+
seenArticles.add(item.article.id);
|
|
421
|
+
if (url) seenUrls.add(url);
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
schema_version: 1,
|
|
426
|
+
policy_version: LOCAL_BRIEFING_POLICY_VERSION,
|
|
427
|
+
publication_id: options.publication_id,
|
|
428
|
+
generated_at: new Date(now).toISOString(),
|
|
429
|
+
personalized,
|
|
430
|
+
items: deduplicated.slice(0, limit).map(({ item }) => item),
|
|
431
|
+
remaining_important_count: deduplicated.slice(limit).filter(({ important }) => important).length
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/preset.ts
|
|
436
|
+
var walnutCreekTimes = definePublication({
|
|
437
|
+
slug: "walnutcreektimes",
|
|
438
|
+
name: "Walnut Creek Times",
|
|
439
|
+
masthead: ["Walnut", "Creek", "Times"],
|
|
440
|
+
region: "Walnut Creek",
|
|
441
|
+
regionLabel: "Walnut Creek",
|
|
442
|
+
towns: ["Walnut Creek", "Rossmoor", "Saranap", "Pleasant Hill"],
|
|
443
|
+
nearbyTowns: ["Martinez", "Concord", "Contra Costa"],
|
|
444
|
+
sections: ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health"],
|
|
445
|
+
description: "Independent local journalism covering civic decisions, downtown commerce, neighborhood life, and community achievements across Walnut Creek, Rossmoor, Saranap, and Pleasant Hill.",
|
|
446
|
+
tagline: "Local stories. Stronger communities.",
|
|
447
|
+
layout: "broadsheet",
|
|
448
|
+
layouts: ["broadsheet"],
|
|
449
|
+
features: { smile: true, jobs: true, classifieds: true },
|
|
450
|
+
modules: { home: ["daily-smile"] }
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// src/cache.ts
|
|
454
|
+
var NEWS_CACHE = {
|
|
455
|
+
feedRevalidateSeconds: 30,
|
|
456
|
+
articleRevalidateSeconds: 60,
|
|
457
|
+
edgeMaxAgeSeconds: 60,
|
|
458
|
+
staleWhileRevalidateSeconds: 300,
|
|
459
|
+
feedTag: "news-feed",
|
|
460
|
+
articleTag: "news-article",
|
|
461
|
+
inventoryTag: "public-news-discovery-v2",
|
|
462
|
+
paths: ["/", "/news"]
|
|
463
|
+
};
|
|
464
|
+
var feedCacheOptions = {
|
|
465
|
+
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
466
|
+
tags: [NEWS_CACHE.feedTag]
|
|
467
|
+
};
|
|
468
|
+
var articleCacheOptions = {
|
|
469
|
+
revalidate: NEWS_CACHE.articleRevalidateSeconds,
|
|
470
|
+
tags: [NEWS_CACHE.articleTag]
|
|
471
|
+
};
|
|
472
|
+
var inventoryCacheOptions = {
|
|
473
|
+
revalidate: NEWS_CACHE.feedRevalidateSeconds,
|
|
474
|
+
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.inventoryTag]
|
|
475
|
+
};
|
|
476
|
+
function edgeCacheControl() {
|
|
477
|
+
return `public, s-maxage=${NEWS_CACHE.edgeMaxAgeSeconds}, stale-while-revalidate=${NEWS_CACHE.staleWhileRevalidateSeconds}`;
|
|
478
|
+
}
|
|
479
|
+
function feedCacheKey(publicationId, before, section) {
|
|
480
|
+
return `${publicationId}:${before ?? ""}:${section ?? ""}`;
|
|
481
|
+
}
|
|
482
|
+
function articleCacheKey(publicationId, slug) {
|
|
483
|
+
return `${publicationId}:${slug}`;
|
|
484
|
+
}
|
|
485
|
+
var NewsError = class extends Error {
|
|
486
|
+
constructor(status, message) {
|
|
487
|
+
super(message);
|
|
488
|
+
this.status = status;
|
|
489
|
+
this.name = "NewsError";
|
|
490
|
+
}
|
|
491
|
+
status;
|
|
492
|
+
};
|
|
493
|
+
function shouldServeLastKnown(error) {
|
|
494
|
+
const status = error instanceof NewsError ? error.status : error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
495
|
+
return status !== 404;
|
|
496
|
+
}
|
|
497
|
+
async function withLastKnown(store, key, load, accept = () => true) {
|
|
498
|
+
try {
|
|
499
|
+
const value = await load();
|
|
500
|
+
if (accept(value)) store.set(key, value);
|
|
501
|
+
return value;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
if (!shouldServeLastKnown(error)) throw error;
|
|
504
|
+
const cached = store.get(key);
|
|
505
|
+
if (cached) return cached;
|
|
506
|
+
throw error;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function secretsMatch(provided, secret) {
|
|
510
|
+
if (!provided || !secret) return false;
|
|
511
|
+
const left = Array.from(provided);
|
|
512
|
+
const right = Array.from(secret);
|
|
513
|
+
let mismatch = left.length === right.length ? 0 : 1;
|
|
514
|
+
const length = Math.max(left.length, right.length);
|
|
515
|
+
for (let index = 0; index < length; index += 1) {
|
|
516
|
+
mismatch |= (left[index]?.codePointAt(0) ?? 0) ^ (right[index]?.codePointAt(0) ?? 0);
|
|
517
|
+
}
|
|
518
|
+
return mismatch === 0;
|
|
519
|
+
}
|
|
520
|
+
function revalidateDecision(input) {
|
|
521
|
+
if (!secretsMatch(input.providedSecret, input.configuredSecret)) {
|
|
522
|
+
return { status: 401, body: { error: "Unauthorized" } };
|
|
523
|
+
}
|
|
524
|
+
const tag = input.tag?.trim() || `${NEWS_CACHE.feedTag}, ${NEWS_CACHE.articleTag}`;
|
|
525
|
+
const path = input.path?.trim() || NEWS_CACHE.paths.join(", ");
|
|
526
|
+
return { status: 200, body: { revalidated: true, tag, path, now: input.now ?? Date.now() } };
|
|
527
|
+
}
|
|
528
|
+
function defaultRevalidateTargets() {
|
|
529
|
+
return {
|
|
530
|
+
tags: [NEWS_CACHE.feedTag, NEWS_CACHE.articleTag, NEWS_CACHE.inventoryTag],
|
|
531
|
+
paths: [...NEWS_CACHE.paths]
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
376
535
|
// src/analytics.ts
|
|
377
536
|
var ANALYTICS_VERSION = 1;
|
|
378
537
|
var ANALYTICS_COOKIE = "napsite_analytics_v1";
|
|
@@ -593,4 +752,4 @@ async function publicationListeningCandidates(items, read, publicationId, public
|
|
|
593
752
|
return output.sort((a, b) => (b.publishedAt ?? "").localeCompare(a.publishedAt ?? ""));
|
|
594
753
|
}
|
|
595
754
|
|
|
596
|
-
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, EDITORIAL_SECTION_ORDER, 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, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, referralCode, resolveCoupon, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
|
755
|
+
export { ANALYTICS_COOKIE, ANALYTICS_VERSION, ArticleProvenance, EDITORIAL_SECTION_ORDER, EVENTS, LOCAL_BRIEFING_POLICY_VERSION, NEWS_CACHE, NewsError, SESSION_IDLE_MS, UTM_FIELDS, UUID, activeNearby, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, composeEdition, composeLocalBriefing, defaultRevalidateTargets, displayHeadline, edgeCacheControl, editorialSections, feedCacheKey, feedCacheOptions, filterNews, inventoryCacheOptions, llmsText, matchesStoryTag, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, referralCode, resolveCoupon, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
|
package/dist/snapshot-file.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-
|
|
2
|
-
import './types-
|
|
1
|
+
import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-Dx4Lv-lB.cjs';
|
|
2
|
+
import './types-BJFCWWi0.cjs';
|
|
3
3
|
|
|
4
4
|
declare function readSnapshotFile(file: string): Promise<PublicationSnapshot | null>;
|
|
5
5
|
/**
|
package/dist/snapshot-file.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-
|
|
2
|
-
import './types-
|
|
1
|
+
import { P as PublicationSnapshot, S as SnapshotHooks, a as SyncedPublication } from './snapshot-DmRw8G_B.js';
|
|
2
|
+
import './types-BJFCWWi0.js';
|
|
3
3
|
|
|
4
4
|
declare function readSnapshotFile(file: string): Promise<PublicationSnapshot | null>;
|
|
5
5
|
/**
|
|
@@ -238,4 +238,4 @@ type EditorialSection = {
|
|
|
238
238
|
items: NewsItem[];
|
|
239
239
|
};
|
|
240
240
|
|
|
241
|
-
export { type ArticleCitation as A, type CouponOffer as C, type EditorialSection as E, type FeatureName as F, type HomepageSelection as H, type NewsPublication as N, type PublicationInput as P, type ReferralSettings as R, type StoryDisplay as S, type TagRef as T, type VerifiedCard as V, type PublicationTheme as a, type
|
|
241
|
+
export { type ArticleCitation as A, type CouponOffer as C, type EditorialSection as E, type FeatureName as F, type HomepageSelection as H, type NewsPublication as N, type PublicationInput as P, type ReferralSettings as R, type StoryDisplay as S, type TagRef as T, type VerifiedCard as V, type PublicationTheme as a, type NewsItem as b, type NearbyUpdate as c, type NewsArticle as d, type CardType as e, type CategoryRef as f, type CouponSettings as g, type CoverImageMetadata as h, FEATURE_NAMES as i, type FeatureFlags as j, type NewsFeed as k, type NewsLayoutId as l, type PublicationModules as m, type VerifiedCardAction as n, type VerifiedCardFact as o, type VerifiedCardImage as p };
|
|
@@ -238,4 +238,4 @@ type EditorialSection = {
|
|
|
238
238
|
items: NewsItem[];
|
|
239
239
|
};
|
|
240
240
|
|
|
241
|
-
export { type ArticleCitation as A, type CouponOffer as C, type EditorialSection as E, type FeatureName as F, type HomepageSelection as H, type NewsPublication as N, type PublicationInput as P, type ReferralSettings as R, type StoryDisplay as S, type TagRef as T, type VerifiedCard as V, type PublicationTheme as a, type
|
|
241
|
+
export { type ArticleCitation as A, type CouponOffer as C, type EditorialSection as E, type FeatureName as F, type HomepageSelection as H, type NewsPublication as N, type PublicationInput as P, type ReferralSettings as R, type StoryDisplay as S, type TagRef as T, type VerifiedCard as V, type PublicationTheme as a, type NewsItem as b, type NearbyUpdate as c, type NewsArticle as d, type CardType as e, type CategoryRef as f, type CouponSettings as g, type CoverImageMetadata as h, FEATURE_NAMES as i, type FeatureFlags as j, type NewsFeed as k, type NewsLayoutId as l, type PublicationModules as m, type VerifiedCardAction as n, type VerifiedCardFact as o, type VerifiedCardImage as p };
|
package/dist/verified-card.d.cts
CHANGED
package/dist/verified-card.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalemule/news-publication",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
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",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"story-review.css"
|
|
110
110
|
],
|
|
111
111
|
"peerDependencies": {
|
|
112
|
-
"@scalemule/react": "0.0.
|
|
112
|
+
"@scalemule/react": "0.0.21",
|
|
113
113
|
"react": ">=18.0.0"
|
|
114
114
|
},
|
|
115
115
|
"peerDependenciesMeta": {
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"prepublishOnly": "node -e \"const v=require('./package.json').version; if(!/^0\\.(0|1)\\.\\d+$/.test(v)){console.error('ERR: SDK '+v+' violates patch-only policy (0.0.x or 0.1.x only). See CONTRIBUTING.md SDK versioning section.');process.exit(1)}\" && npm run lint && npm test && npm run build"
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
|
-
"@scalemule/react": "0.0.
|
|
132
|
+
"@scalemule/react": "0.0.21",
|
|
133
133
|
"@testing-library/react": "^16.3.3",
|
|
134
134
|
"@types/node": "^22.15.0",
|
|
135
135
|
"@types/react": "^19.1.0",
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
},
|
|
163
163
|
"dependencies": {
|
|
164
164
|
"@scalemule/compliance": "^0.0.5",
|
|
165
|
-
"@scalemule/react": "0.0.
|
|
165
|
+
"@scalemule/react": "0.0.21",
|
|
166
166
|
"@scalemule/sdk": "0.0.75"
|
|
167
167
|
}
|
|
168
168
|
}
|