@scalemule/news-publication 0.0.47 → 0.0.49

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.
Files changed (73) hide show
  1. package/README.md +28 -0
  2. package/dist/{chunk-GB36YM56.js → chunk-2MEYHM7M.js} +11 -1
  3. package/dist/chunk-4JRWZXAP.js +62 -0
  4. package/dist/chunk-MBBPEFDA.js +124 -0
  5. package/dist/chunk-TTLM7YOH.js +444 -0
  6. package/dist/chunk-ZXH3O537.js +251 -0
  7. package/dist/community-maps-view.d.cts +3 -3
  8. package/dist/community-maps-view.d.ts +3 -3
  9. package/dist/cookie-consent-banner.d.cts +2 -2
  10. package/dist/cookie-consent-banner.d.ts +2 -2
  11. package/dist/cookie-settings-view.d.cts +2 -2
  12. package/dist/cookie-settings-view.d.ts +2 -2
  13. package/dist/index.cjs +74 -3
  14. package/dist/index.d.cts +12 -4
  15. package/dist/index.d.ts +12 -4
  16. package/dist/index.js +49 -6
  17. package/dist/marketplace-forms.d.cts +3 -3
  18. package/dist/marketplace-forms.d.ts +3 -3
  19. package/dist/marketplace-view.d.cts +5 -5
  20. package/dist/marketplace-view.d.ts +5 -5
  21. package/dist/{meet-RWmfj3I3.d.cts → meet-BWpxY20X.d.ts} +5 -0
  22. package/dist/{meet-RWmfj3I3.d.ts → meet-CO9Unz5M.d.cts} +5 -0
  23. package/dist/meet-forms.d.cts +7 -6
  24. package/dist/meet-forms.d.ts +7 -6
  25. package/dist/meet-forms.js +2 -1
  26. package/dist/meet-view.cjs +22 -2
  27. package/dist/meet-view.d.cts +7 -5
  28. package/dist/meet-view.d.ts +7 -5
  29. package/dist/meet-view.js +21 -4
  30. package/dist/network-bar.d.cts +2 -2
  31. package/dist/network-bar.d.ts +2 -2
  32. package/dist/network-copyright.d.cts +2 -2
  33. package/dist/network-copyright.d.ts +2 -2
  34. package/dist/pets-auth-server.cjs +212 -0
  35. package/dist/pets-auth-server.d.cts +17 -0
  36. package/dist/pets-auth-server.d.ts +17 -0
  37. package/dist/pets-auth-server.js +155 -0
  38. package/dist/pets-forms.cjs +762 -0
  39. package/dist/pets-forms.d.cts +26 -0
  40. package/dist/pets-forms.d.ts +26 -0
  41. package/dist/pets-forms.js +712 -0
  42. package/dist/pets-interactions.cjs +332 -0
  43. package/dist/pets-interactions.d.cts +20 -0
  44. package/dist/pets-interactions.d.ts +20 -0
  45. package/dist/pets-interactions.js +319 -0
  46. package/dist/pets-publication.cjs +818 -0
  47. package/dist/pets-publication.d.cts +36 -0
  48. package/dist/pets-publication.d.ts +36 -0
  49. package/dist/pets-publication.js +177 -0
  50. package/dist/pets-server.cjs +289 -0
  51. package/dist/pets-server.d.cts +42 -0
  52. package/dist/pets-server.d.ts +42 -0
  53. package/dist/pets-server.js +2 -0
  54. package/dist/pets-view.cjs +541 -0
  55. package/dist/pets-view.d.cts +56 -0
  56. package/dist/pets-view.d.ts +56 -0
  57. package/dist/pets-view.js +4 -0
  58. package/dist/pets.cjs +138 -0
  59. package/dist/pets.d.cts +127 -0
  60. package/dist/pets.d.ts +127 -0
  61. package/dist/pets.js +1 -0
  62. package/dist/sponsorship-view.d.cts +2 -2
  63. package/dist/sponsorship-view.d.ts +2 -2
  64. package/dist/sponsorship-view.js +2 -62
  65. package/dist/story-review.d.cts +6 -6
  66. package/dist/story-review.d.ts +6 -6
  67. package/dist/theme-toggle.d.cts +2 -2
  68. package/dist/theme-toggle.d.ts +2 -2
  69. package/dist/verified-card.d.cts +3 -3
  70. package/dist/verified-card.d.ts +3 -3
  71. package/package.json +40 -3
  72. package/pets.css +538 -0
  73. package/tokens/broadsheet.css +46 -0
@@ -0,0 +1,251 @@
1
+ import { PET_UUID, petReferences, PET_SLUG } from './chunk-MBBPEFDA.js';
2
+ import { buildClientContextHeaders } from '@scalemule/sdk';
3
+
4
+ var PetRequestError = class extends Error {
5
+ constructor(status, message) {
6
+ super(message);
7
+ this.status = status;
8
+ }
9
+ status;
10
+ };
11
+ var petPrivateHeaders = {
12
+ "cache-control": "private, no-store",
13
+ "x-robots-tag": "noindex, nofollow",
14
+ "referrer-policy": "no-referrer",
15
+ "x-content-type-options": "nosniff"
16
+ };
17
+ async function bounded(request, max) {
18
+ if (Number(request.headers.get("content-length")) > max)
19
+ throw new PetRequestError(413, "The submission is too large.");
20
+ const reader = request.body?.getReader();
21
+ if (!reader) return new Uint8Array();
22
+ const parts = [];
23
+ let size = 0;
24
+ try {
25
+ for (; ; ) {
26
+ const { done, value } = await reader.read();
27
+ if (done) break;
28
+ size += value.length;
29
+ if (size > max) {
30
+ await reader.cancel();
31
+ throw new PetRequestError(413, "The submission is too large.");
32
+ }
33
+ parts.push(value);
34
+ }
35
+ } finally {
36
+ reader.releaseLock();
37
+ }
38
+ return Buffer.concat(parts);
39
+ }
40
+ function config(scope) {
41
+ const url = new URL(scope.gatewayUrl);
42
+ if (!scope.apiKey || !PET_SLUG.test(scope.collectionSlug) || url.username || url.password || url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1"].includes(url.hostname)))
43
+ throw new PetRequestError(503, "The pet community is not available yet.");
44
+ return url.origin;
45
+ }
46
+ async function petRequest(scope, path, init = {}, fetcher = fetch) {
47
+ const gateway = config(scope);
48
+ if (!path.startsWith("/v1/") || path.includes("..") || /[\\\r\n]/.test(path))
49
+ throw new Error("Invalid platform path");
50
+ const response = await fetcher(gateway + path, {
51
+ method: init.method || "GET",
52
+ headers: {
53
+ ...buildClientContextHeaders(scope.clientContext),
54
+ "x-api-key": scope.apiKey,
55
+ "content-type": "application/json",
56
+ ...scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {}
57
+ },
58
+ body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
59
+ cache: "no-store",
60
+ redirect: "error",
61
+ signal: AbortSignal.timeout(15e3)
62
+ });
63
+ if (!response.ok)
64
+ throw new PetRequestError(
65
+ response.status,
66
+ response.status === 401 ? "Please sign in to continue." : response.status === 404 ? "This pet resource is not available." : response.status === 409 ? "This contribution changed. Reload before saving." : response.status === 429 ? "Please wait before trying again." : "We couldn\u2019t complete that request. Please try again."
67
+ );
68
+ const json = await response.json();
69
+ return json?.data ?? json;
70
+ }
71
+ function createPublicationPetsHandler(options) {
72
+ const fetcher = options.fetch || fetch;
73
+ return async function handler(request, path) {
74
+ const json = (data, status = 200) => Response.json(data, { status, headers: petPrivateHeaders });
75
+ try {
76
+ const scope = await options.resolve(request), gateway = config(scope), method = request.method;
77
+ const root = `/v1/knowledge/community-maps/collections/${scope.collectionSlug}`;
78
+ const id = path[0] && PET_UUID.test(path[0]);
79
+ const isMedia = id && path.length === 3 && path[1] === "media" && PET_UUID.test(path[2]);
80
+ const upload = path.length === 3 && path[0] === "submissions" && PET_UUID.test(path[1]) && path[2] === "media";
81
+ const ownMedia = path.length === 4 && path[0] === "submissions" && PET_UUID.test(path[1]) && path[2] === "media" && PET_UUID.test(path[3]);
82
+ const own = path[0] === "mine" && (path.length === 1 || path.length === 2 && PET_UUID.test(path[1]));
83
+ const preferences = path.length === 1 && path[0] === "preferences";
84
+ const reaction = id && path.length === 2 && path[1] === "reaction";
85
+ const report = id && path.length === 2 && path[1] === "reports";
86
+ const publicRead = method === "GET" && (!path.length || id && path.length === 1 || isMedia || path.length === 1 && path[0] === "community");
87
+ const read = publicRead || method === "GET" && (own || ownMedia || preferences || reaction);
88
+ const write = method === "POST" && (!path.length || upload || report || id && path.length === 2 && path[1] === "reunited") || method === "PUT" && (preferences || reaction) || ["PUT", "DELETE"].includes(method) && id && path.length === 1;
89
+ if (!read && !write) return json({ error: "Route not found" }, 404);
90
+ if (write && (!scope.writesEnabled || request.headers.get("origin") !== new URL(scope.origin).origin))
91
+ return json(
92
+ { error: "Continue from the pet community\u2019s official website." },
93
+ 403
94
+ );
95
+ if ((write || own || ownMedia || preferences || reaction) && !scope.accessToken)
96
+ return json({ error: "Sign in to continue." }, 401);
97
+ let endpoint = root + (path[0] === "community" ? "" : upload || ownMedia ? "/" + path.join("/") : "/pets" + (path.length ? "/" + path.join("/") : ""));
98
+ const query = new URLSearchParams();
99
+ if (method === "GET" && !path.length)
100
+ for (const key of [
101
+ "area_id",
102
+ "kind",
103
+ "after",
104
+ "week",
105
+ "limit",
106
+ "category",
107
+ "species",
108
+ "event_type",
109
+ "cost",
110
+ "starts_after",
111
+ "starts_before"
112
+ ]) {
113
+ const value = new URL(request.url).searchParams.get(key);
114
+ if (value) query.set(key, value);
115
+ }
116
+ if (own) {
117
+ const after = new URL(request.url).searchParams.get("after");
118
+ if (after && PET_UUID.test(after)) query.set("after", after);
119
+ }
120
+ if (query.size) endpoint += "?" + query;
121
+ if (isMedia) {
122
+ const imageQuery = new URLSearchParams();
123
+ const sourceQuery = new URL(request.url).searchParams;
124
+ if (["640", "1280"].includes(sourceQuery.get("width") || ""))
125
+ imageQuery.set("width", sourceQuery.get("width"));
126
+ if (["jpeg", "webp"].includes(sourceQuery.get("format") || ""))
127
+ imageQuery.set("format", sourceQuery.get("format"));
128
+ if (imageQuery.size) endpoint += "?" + imageQuery;
129
+ }
130
+ if (isMedia || upload || ownMedia) {
131
+ let body2;
132
+ const type = request.headers.get("content-type") || "";
133
+ if (upload) {
134
+ if (!type.startsWith("multipart/form-data;"))
135
+ throw new PetRequestError(415, "Choose a photograph.");
136
+ body2 = await bounded(request, 9 * 1024 * 1024);
137
+ }
138
+ const response = await fetcher(gateway + endpoint, {
139
+ method,
140
+ headers: {
141
+ ...buildClientContextHeaders(scope.clientContext),
142
+ "x-api-key": scope.apiKey,
143
+ ...scope.accessToken ? { authorization: `Bearer ${scope.accessToken}` } : {},
144
+ ...upload ? { "content-type": type } : {}
145
+ },
146
+ body: body2,
147
+ cache: "no-store",
148
+ redirect: "error",
149
+ signal: AbortSignal.timeout(45e3)
150
+ });
151
+ if (!response.ok) {
152
+ await response.body?.cancel();
153
+ return json(
154
+ {
155
+ error: "The photograph is unavailable. Check its size and try again."
156
+ },
157
+ response.status >= 500 ? 503 : response.status
158
+ );
159
+ }
160
+ if (upload) return json(await response.json());
161
+ const imageType = response.headers.get("content-type")?.split(";")[0] || "";
162
+ if (!["image/png", "image/jpeg", "image/webp"].includes(imageType)) {
163
+ await response.body?.cancel();
164
+ return json({ error: "Photograph unavailable" }, 404);
165
+ }
166
+ return new Response(response.body, {
167
+ headers: { ...petPrivateHeaders, "content-type": imageType }
168
+ });
169
+ }
170
+ let body;
171
+ if (write) {
172
+ if (!request.headers.get("content-type")?.startsWith("application/json"))
173
+ throw new PetRequestError(415, "Use the pet contribution form.");
174
+ try {
175
+ body = JSON.parse(
176
+ new TextDecoder().decode(await bounded(request, 32768))
177
+ );
178
+ } catch (error) {
179
+ if (error instanceof PetRequestError) throw error;
180
+ throw new PetRequestError(400, "Check the contribution fields.");
181
+ }
182
+ }
183
+ return json({
184
+ data: await petRequest(scope, endpoint, { method, body }, fetcher)
185
+ });
186
+ } catch (error) {
187
+ return json(
188
+ {
189
+ error: error instanceof PetRequestError ? error.message : "The pet community is temporarily unavailable."
190
+ },
191
+ error instanceof PetRequestError ? error.status : 503
192
+ );
193
+ }
194
+ };
195
+ }
196
+ async function resolvePetReferences(scope, metadata, fetcher = fetch) {
197
+ const references = petReferences(metadata).filter(
198
+ (ref) => ref.collection_slug === scope.collectionSlug
199
+ );
200
+ const results = await Promise.all(
201
+ references.map(async (ref) => {
202
+ try {
203
+ const resource = await petRequest(
204
+ { ...scope, accessToken: void 0 },
205
+ `/v1/knowledge/community-maps/collections/${ref.collection_slug}/pets/${ref.id}`,
206
+ {},
207
+ fetcher
208
+ );
209
+ return resource.kind === ref.kind ? resource : null;
210
+ } catch (error) {
211
+ if (error instanceof PetRequestError && [403, 404].includes(error.status))
212
+ return null;
213
+ throw error;
214
+ }
215
+ })
216
+ );
217
+ return results.filter(
218
+ (resource) => resource !== null
219
+ );
220
+ }
221
+ async function petAcknowledgments(scope, community, productPath = "/pets", fetcher = fetch) {
222
+ if (!scope.publicationId || !PET_UUID.test(scope.publicationId)) return [];
223
+ try {
224
+ const groups = await Promise.all(
225
+ ["community_partner", "community_guide"].map(
226
+ async (placement) => {
227
+ const query = new URLSearchParams({
228
+ placement_type: placement,
229
+ product_path: productPath
230
+ });
231
+ if (community) query.set("community", community);
232
+ const response = await petRequest(
233
+ { ...scope, accessToken: void 0 },
234
+ `/v1/blog/publications/${scope.publicationId}/sponsorships/delivery?${query}`,
235
+ {},
236
+ fetcher
237
+ );
238
+ return response.items.filter(
239
+ (item) => item.publication_id === scope.publicationId && item.placement_type === placement
240
+ );
241
+ }
242
+ )
243
+ );
244
+ return groups.flat().slice(0, 2);
245
+ } catch {
246
+ console.warn("paw.sponsorship_delivery_unavailable");
247
+ return [];
248
+ }
249
+ }
250
+
251
+ export { PetRequestError, createPublicationPetsHandler, petAcknowledgments, petPrivateHeaders, petRequest, resolvePetReferences };
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import { CommunityMapCollection, CommunityMapFeature, CommunityCityAtlasConfig } from '@scalemule/react/community-maps';
3
3
  export { c as communityMapEmbeds } from './community-maps-DKcYhQYN.cjs';
4
4
 
@@ -13,11 +13,11 @@ interface PublicationCommunityMapPageProps {
13
13
  atlas?: CommunityCityAtlasConfig;
14
14
  }
15
15
  /** The newspaper supplies its chrome and inherited type. The platform owns map behavior. */
16
- declare function PublicationCommunityMapPage({ collection, feature, mode, areaId, correctionFor, basePath, apiPath, atlas, }: PublicationCommunityMapPageProps): react.JSX.Element;
16
+ declare function PublicationCommunityMapPage({ collection, feature, mode, areaId, correctionFor, basePath, apiPath, atlas, }: PublicationCommunityMapPageProps): React.JSX.Element;
17
17
 
18
18
  declare function CommunityMapArticleEmbeds({ metadata, apiPath, }: {
19
19
  metadata: unknown;
20
20
  apiPath?: string;
21
- }): react.JSX.Element;
21
+ }): React.JSX.Element;
22
22
 
23
23
  export { CommunityMapArticleEmbeds, PublicationCommunityMapPage, type PublicationCommunityMapPageProps };
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import { CommunityMapCollection, CommunityMapFeature, CommunityCityAtlasConfig } from '@scalemule/react/community-maps';
3
3
  export { c as communityMapEmbeds } from './community-maps-DKcYhQYN.js';
4
4
 
@@ -13,11 +13,11 @@ interface PublicationCommunityMapPageProps {
13
13
  atlas?: CommunityCityAtlasConfig;
14
14
  }
15
15
  /** The newspaper supplies its chrome and inherited type. The platform owns map behavior. */
16
- declare function PublicationCommunityMapPage({ collection, feature, mode, areaId, correctionFor, basePath, apiPath, atlas, }: PublicationCommunityMapPageProps): react.JSX.Element;
16
+ declare function PublicationCommunityMapPage({ collection, feature, mode, areaId, correctionFor, basePath, apiPath, atlas, }: PublicationCommunityMapPageProps): React.JSX.Element;
17
17
 
18
18
  declare function CommunityMapArticleEmbeds({ metadata, apiPath, }: {
19
19
  metadata: unknown;
20
20
  apiPath?: string;
21
- }): react.JSX.Element;
21
+ }): React.JSX.Element;
22
22
 
23
23
  export { CommunityMapArticleEmbeds, PublicationCommunityMapPage, type PublicationCommunityMapPageProps };
@@ -1,4 +1,4 @@
1
- import react__default from 'react';
1
+ import React__default from 'react';
2
2
 
3
3
  interface CookieConsentBannerProps {
4
4
  /** Title displayed in banner. Default: "Help Us Improve Our Website with Cookies" */
@@ -20,6 +20,6 @@ interface CookieConsentBannerProps {
20
20
  /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
21
  consentHubUrl?: string;
22
22
  }
23
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): react__default.JSX.Element | null;
23
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): React__default.JSX.Element | null;
24
24
 
25
25
  export { CookieConsentBanner, type CookieConsentBannerProps };
@@ -1,4 +1,4 @@
1
- import react__default from 'react';
1
+ import React__default from 'react';
2
2
 
3
3
  interface CookieConsentBannerProps {
4
4
  /** Title displayed in banner. Default: "Help Us Improve Our Website with Cookies" */
@@ -20,6 +20,6 @@ interface CookieConsentBannerProps {
20
20
  /** URL of the central network consent iframe bridge. Default: "https://bayareachronicle.com/consent-bridge.html" */
21
21
  consentHubUrl?: string;
22
22
  }
23
- declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): react__default.JSX.Element | null;
23
+ declare function CookieConsentBanner({ title, text, settingsUrl, className, publicationSlug, complianceEndpoint, countryCode, forceShow, consentHubUrl, }: CookieConsentBannerProps): React__default.JSX.Element | null;
24
24
 
25
25
  export { CookieConsentBanner, type CookieConsentBannerProps };
@@ -1,4 +1,4 @@
1
- import react__default from 'react';
1
+ import React__default from 'react';
2
2
 
3
3
  interface CookieSettingsViewProps {
4
4
  /** The publication display name (e.g., "San Ramon Times") */
@@ -10,6 +10,6 @@ interface CookieSettingsViewProps {
10
10
  /** Optional publication slug for metadata */
11
11
  publicationSlug?: string;
12
12
  }
13
- declare function CookieSettingsView({ publicationName, region, complianceEndpoint, publicationSlug, }: CookieSettingsViewProps): react__default.JSX.Element;
13
+ declare function CookieSettingsView({ publicationName, region, complianceEndpoint, publicationSlug, }: CookieSettingsViewProps): React__default.JSX.Element;
14
14
 
15
15
  export { CookieSettingsView, type CookieSettingsViewProps };
@@ -1,4 +1,4 @@
1
- import react__default from 'react';
1
+ import React__default from 'react';
2
2
 
3
3
  interface CookieSettingsViewProps {
4
4
  /** The publication display name (e.g., "San Ramon Times") */
@@ -10,6 +10,6 @@ interface CookieSettingsViewProps {
10
10
  /** Optional publication slug for metadata */
11
11
  publicationSlug?: string;
12
12
  }
13
- declare function CookieSettingsView({ publicationName, region, complianceEndpoint, publicationSlug, }: CookieSettingsViewProps): react__default.JSX.Element;
13
+ declare function CookieSettingsView({ publicationName, region, complianceEndpoint, publicationSlug, }: CookieSettingsViewProps): React__default.JSX.Element;
14
14
 
15
15
  export { CookieSettingsView, type CookieSettingsViewProps };
package/dist/index.cjs CHANGED
@@ -90,6 +90,48 @@ var EDITORIAL_SECTION_ORDER = [
90
90
  "Schools",
91
91
  "Opinion"
92
92
  ];
93
+ var SECTION_ALIASES = {
94
+ "Business": "Local businesses",
95
+ "Government": "Civic life",
96
+ "Arts & Culture": "Community",
97
+ "Food & drink": "Food & places",
98
+ "Things to do": "Events",
99
+ "Sports": "Community",
100
+ "Education": "Schools",
101
+ "Public Safety": "Civic life",
102
+ "Transit": "Civic life",
103
+ "Environment": "Civic life",
104
+ "Recreation": "Community"
105
+ };
106
+ function resolveSection(item) {
107
+ const explicit = item.section?.trim();
108
+ if (explicit && explicit !== "Local news") {
109
+ return SECTION_ALIASES[explicit] ?? explicit;
110
+ }
111
+ const descriptors = [
112
+ ...(item.tags ?? []).map((t) => t.name.toLowerCase()),
113
+ ...(item.categories ?? []).map((c) => c.name.toLowerCase())
114
+ ];
115
+ for (const desc of descriptors) {
116
+ if (/\b(health|wellness|medical|hospital|clinic|doctor|pharmacy|veterinar|pet care|animal welfare|vaccin)\b/i.test(desc)) return "Health";
117
+ if (/\b(real estate|housing|home|homes|mortgage|tenant|tenants|apartment|apartments|property|bmr)\b/i.test(desc)) return "Real Estate";
118
+ if (/\b(civic|government|council|election|ballot|measure|transit|traffic|police|fire|firefighter|wildfire|safety|policy|supervisor)\b/i.test(desc)) return "Civic life";
119
+ if (/\b(business|retail|commerce|commercial|downtown|store|shop|merchants?|chamber of commerce)\b/i.test(desc)) return "Local businesses";
120
+ if (/\b(event|events|festival|festivals|fair|fairs|concert|concerts|music|workshop|workshops|things to do|exhibition|performance)\b/i.test(desc)) return "Events";
121
+ if (/\b(school|schools|education|student|students|teacher|teachers|campus|college|university)\b/i.test(desc)) return "Schools";
122
+ if (/\b(food|dining|restaurant|restaurants|wine|winery|brewery|bakery|cafe|coffee|cuisine|chef)\b/i.test(desc)) return "Food & places";
123
+ if (/\b(community|volunteer|volunteers|library|libraries|neighborhood|nonprofit|charity|youth|senior|seniors)\b/i.test(desc)) return "Community";
124
+ }
125
+ const text = `${item.title} ${item.excerpt ?? ""}`;
126
+ if (/\b(health|hospitals?|clinics?|medical|doctors?|pharmac(?:y|ies)|wellness|veterinar(?:y|ian)|pet care|animal welfare|vaccinat(?:ion|ions)|patients?)\b/iu.test(text)) return "Health";
127
+ if (/\b(real estate|homeownership|mortgage|housing|tenants?|apartments?|below-market rate|home sales?|condos?)\b/iu.test(text)) return "Real Estate";
128
+ if (/\b(city council|planning commission|board of supervisors|ballot|measure [a-z0-9]|election|transit|traffic|police|firefighter|wildfire|evacuation|pedestrian safety|city manager|public works)\b/iu.test(text)) return "Civic life";
129
+ if (/\b(storefront|retail|downtown business|chamber of commerce|opens? doors?|boutique|merchants?|shoppers?|commercial space)\b/iu.test(text)) return "Local businesses";
130
+ if (/\b(festival|parade|fair|concert|workshop|exhibition|showcase|celebration|weigh-off|halloween|trunk-or-treat)\b/iu.test(text)) return "Events";
131
+ if (/\b(schools?|students?|teachers?|campuses?|classrooms?|high school|elementary|school district)\b/iu.test(text)) return "Schools";
132
+ if (/\b(restaurants?|cafes?|baker(?:y|ies)|winer(?:y|ies)|brewer(?:y|ies)|dining|chefs?|menus?)\b/iu.test(text)) return "Food & places";
133
+ return "Community";
134
+ }
93
135
  function orderedSections(sections2, order = EDITORIAL_SECTION_ORDER) {
94
136
  return sections2.filter((section) => section.name !== "Local news").slice().sort((left, right) => {
95
137
  const leftRank = order.indexOf(left.name);
@@ -111,8 +153,8 @@ function sectionDefinition(name) {
111
153
  function editorialSections(items) {
112
154
  const groups = /* @__PURE__ */ new Map();
113
155
  for (const item of items) {
114
- const name = item.section?.trim() || "Local news";
115
- groups.set(name, [...groups.get(name) ?? [], item]);
156
+ const name = resolveSection(item);
157
+ groups.set(name, [...groups.get(name) ?? [], { ...item, section: name }]);
116
158
  }
117
159
  return Array.from(groups, ([name, articles]) => ({ ...sectionDefinition(name), items: articles }));
118
160
  }
@@ -710,6 +752,26 @@ function referralCode(publication, url) {
710
752
  }
711
753
  }
712
754
 
755
+ // src/pets.ts
756
+ function preparePawWalk(value) {
757
+ if (!Number.isInteger(value.duration_minutes) || value.duration_minutes < 10 || value.duration_minutes > 240)
758
+ throw new Error("Choose a walk duration from 10 to 240 minutes.");
759
+ if (value.distance_km !== void 0 && (!Number.isFinite(value.distance_km) || value.distance_km < 0 || value.distance_km > 30))
760
+ throw new Error("Choose a walking distance from 0 to 30 km.");
761
+ if (value.leash !== "required" && value.leash !== "venue_rules")
762
+ throw new Error("Choose the leash requirements.");
763
+ for (const field of [value.size_guidance, value.accessibility])
764
+ if (typeof field !== "string" || Array.from(field).length > 500)
765
+ throw new Error("Keep walk guidance within 500 characters.");
766
+ return {
767
+ duration_minutes: value.duration_minutes,
768
+ distance_km: value.distance_km,
769
+ leash: value.leash,
770
+ size_guidance: value.size_guidance,
771
+ accessibility: value.accessibility
772
+ };
773
+ }
774
+
713
775
  // src/meet.ts
714
776
  var MEET_ACTIVITIES = [
715
777
  { value: "coffee", label: "Coffee" },
@@ -769,6 +831,13 @@ function prepareMeetup(input, places, now = /* @__PURE__ */ new Date()) {
769
831
  if (!notes.ok) throw new Error(notes.reason);
770
832
  const capacity = Number(input.capacity ?? 6);
771
833
  if (!Number.isInteger(capacity) || capacity < 2 || capacity > 12) throw new Error("Choose a group size from 2 to 12.");
834
+ const walk = activity === "dogs" && input.walk ? preparePawWalk(input.walk) : void 0;
835
+ if (walk) for (const guidance of [walk.size_guidance, walk.accessibility]) {
836
+ if (guidance.trim()) {
837
+ const screened = screenMessage(guidance);
838
+ if (!screened.ok) throw new Error(screened.reason);
839
+ }
840
+ }
772
841
  return {
773
842
  activity,
774
843
  activityLabel: label,
@@ -777,7 +846,8 @@ function prepareMeetup(input, places, now = /* @__PURE__ */ new Date()) {
777
846
  placeDetail: place.detail,
778
847
  startsAt: new Date(starts).toISOString(),
779
848
  notes: notes.text,
780
- capacity
849
+ capacity,
850
+ ...walk ? { walk } : {}
781
851
  };
782
852
  }
783
853
 
@@ -3492,6 +3562,7 @@ exports.renderArticleBlocksToHtml = renderArticleBlocksToHtml;
3492
3562
  exports.resolveCoupon = resolveCoupon;
3493
3563
  exports.resolveEffectiveTheme = resolveEffectiveTheme;
3494
3564
  exports.resolveLayout = resolveLayout;
3565
+ exports.resolveSection = resolveSection;
3495
3566
  exports.revalidateDecision = revalidateDecision;
3496
3567
  exports.safeSponsorUrl = safeSponsorUrl;
3497
3568
  exports.sanitizeInlineMarkdown = sanitizeInlineMarkdown;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
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
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
- 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';
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-CO9Unz5M.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
  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';
@@ -11,12 +11,13 @@ export { ThemeToggle, ThemeToggleProps } from './theme-toggle.cjs';
11
11
  export { EDITORIAL_PRINCIPLES, EditorialPrinciple, NETWORK_PARENT, NetworkParent, PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './network.cjs';
12
12
  export { LocalNetworkBar, LocalNetworkBarProps } from './network-bar.cjs';
13
13
  export { NetworkCopyrightNotice, NetworkCopyrightNoticeProps } from './network-copyright.cjs';
14
- import * as react from 'react';
14
+ import * as React from 'react';
15
15
  export { VerifiedCardComponent, VerifiedCardSection, getArticleCards, isSafeActionUrl } from './verified-card.cjs';
16
16
  export { C as CommunityMapArticleEmbed, c as communityMapEmbeds } from './community-maps-DKcYhQYN.cjs';
17
17
  export { C as ContentBlock, S as StructuredDocument, a as StructuredMedia, f as formatArtworkCaption, p as parseDocumentBlocks, r as renderArticleBlocksToHtml, s as sanitizeInlineMarkdown } from './article-blocks-DsabIDmH.cjs';
18
18
  import { M as Measurement } from './sponsorships-CtnBe3S8.cjs';
19
19
  export { a as Campaign, b as CampaignStatus, c as CampaignTarget, C as Creative, d as Placement, e as PlacementType, P as PublicAcknowledgment, S as SPONSORSHIP_OFFERINGS, f as SPONSORSHIP_POLICY, g as SponsorParty, p as placementAvailable, s as safeSponsorUrl } from './sponsorships-CtnBe3S8.cjs';
20
+ import './pets.cjs';
20
21
 
21
22
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
22
23
  declare const BROADSHEET_THEME: PublicationTheme;
@@ -221,6 +222,13 @@ declare function defaultRevalidateTargets(): {
221
222
  * "Opinion" are reserved so future sections slot in without a release.
222
223
  */
223
224
  declare const EDITORIAL_SECTION_ORDER: readonly ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health", "Schools", "Opinion"];
225
+ /**
226
+ * Resolves a story's canonical editorial section.
227
+ * Preserves explicitly assigned sections (normalizing known aliases).
228
+ * If unassigned, intelligently infers section desk from tags, categories, or keywords
229
+ * so stories are never lost to unrendered buckets.
230
+ */
231
+ declare function resolveSection(item: NewsItem): string;
224
232
  /** Sorts sections into the canonical order, dropping the implicit "Local news" bucket. */
225
233
  declare function orderedSections<T extends {
226
234
  name: string;
@@ -417,7 +425,7 @@ declare function subscribeTheme(listener: () => void): () => void;
417
425
  declare function themeInitScript(): string;
418
426
 
419
427
  /** A reader projection, never an internal reporting package. */
420
- declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): react.JSX.Element | null;
428
+ declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): React.JSX.Element | null;
421
429
  declare function readerSources(input: unknown): {
422
430
  url: string;
423
431
  title?: string;
@@ -506,4 +514,4 @@ interface SponsorshipReport {
506
514
  /** Shared by the newsroom print/export view and the repeatable PDF renderer. */
507
515
  declare function campaignReportHtml(report: SponsorshipReport): string;
508
516
 
509
- 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, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, 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 };
517
+ 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, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
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
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
- 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';
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-BWpxY20X.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
  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';
@@ -11,12 +11,13 @@ export { ThemeToggle, ThemeToggleProps } from './theme-toggle.js';
11
11
  export { EDITORIAL_PRINCIPLES, EditorialPrinciple, NETWORK_PARENT, NetworkParent, PublicationNetworkItem, communityPublications, findPublicationByHost, findPublicationBySlug, getNetworkPublicationUrl, listedPublications, localPublications } from './network.js';
12
12
  export { LocalNetworkBar, LocalNetworkBarProps } from './network-bar.js';
13
13
  export { NetworkCopyrightNotice, NetworkCopyrightNoticeProps } from './network-copyright.js';
14
- import * as react from 'react';
14
+ import * as React from 'react';
15
15
  export { VerifiedCardComponent, VerifiedCardSection, getArticleCards, isSafeActionUrl } from './verified-card.js';
16
16
  export { C as CommunityMapArticleEmbed, c as communityMapEmbeds } from './community-maps-DKcYhQYN.js';
17
17
  export { C as ContentBlock, S as StructuredDocument, a as StructuredMedia, f as formatArtworkCaption, p as parseDocumentBlocks, r as renderArticleBlocksToHtml, s as sanitizeInlineMarkdown } from './article-blocks-DsabIDmH.js';
18
18
  import { M as Measurement } from './sponsorships-CtnBe3S8.js';
19
19
  export { a as Campaign, b as CampaignStatus, c as CampaignTarget, C as Creative, d as Placement, e as PlacementType, P as PublicAcknowledgment, S as SPONSORSHIP_OFFERINGS, f as SPONSORSHIP_POLICY, g as SponsorParty, p as placementAvailable, s as safeSponsorUrl } from './sponsorships-CtnBe3S8.js';
20
+ import './pets.js';
20
21
 
21
22
  /** Walnut Creek Times broadsheet colors. A paper overrides only what it changes. */
22
23
  declare const BROADSHEET_THEME: PublicationTheme;
@@ -221,6 +222,13 @@ declare function defaultRevalidateTargets(): {
221
222
  * "Opinion" are reserved so future sections slot in without a release.
222
223
  */
223
224
  declare const EDITORIAL_SECTION_ORDER: readonly ["Civic life", "Community", "Local businesses", "Real Estate", "Food & places", "Events", "Health", "Schools", "Opinion"];
225
+ /**
226
+ * Resolves a story's canonical editorial section.
227
+ * Preserves explicitly assigned sections (normalizing known aliases).
228
+ * If unassigned, intelligently infers section desk from tags, categories, or keywords
229
+ * so stories are never lost to unrendered buckets.
230
+ */
231
+ declare function resolveSection(item: NewsItem): string;
224
232
  /** Sorts sections into the canonical order, dropping the implicit "Local news" bucket. */
225
233
  declare function orderedSections<T extends {
226
234
  name: string;
@@ -417,7 +425,7 @@ declare function subscribeTheme(listener: () => void): () => void;
417
425
  declare function themeInitScript(): string;
418
426
 
419
427
  /** A reader projection, never an internal reporting package. */
420
- declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): react.JSX.Element | null;
428
+ declare function ArticleProvenance({ metadata }: Pick<NewsArticle, 'metadata'>): React.JSX.Element | null;
421
429
  declare function readerSources(input: unknown): {
422
430
  url: string;
423
431
  title?: string;
@@ -506,4 +514,4 @@ interface SponsorshipReport {
506
514
  /** Shared by the newsroom print/export view and the repeatable PDF renderer. */
507
515
  declare function campaignReportHtml(report: SponsorshipReport): string;
508
516
 
509
- 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, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, 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 };
517
+ 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, Measurement, NEWS_CACHE, NearbyUpdate, NewsArticle, NewsError, NewsItem, NewsPublication, PublicationInput, PublicationTheme, SESSION_IDLE_MS, type SponsorshipReport, THEME_COOKIE, THEME_EVENT, THEME_MAX_AGE_SECONDS, THEME_STORAGE_KEY, type ThemeMode, UTM_FIELDS, UUID, activeNearby, applyTheme, articleCacheKey, articleCacheOptions, articleMeta, articleUrl, attribution, briefSummary, campaignReportHtml, clearTheme, composeEdition, composeLocalBriefing, defaultRevalidateTargets, definePublication, displayHeadline, edgeCacheControl, editorialSections, featureEnabled, feedCacheKey, feedCacheOptions, filterNews, getStoredTheme, getSystemTheme, inventoryCacheOptions, llmsText, matchesStoryTag, measurementConsent, nearbyUpdates, newsArticleJsonLd, normalizedText, orderedSections, publicHttps, publicUrl, publicationJsonLd, publicationListeningCandidates, publicationListeningTrack, publicationMeta, readerSources, recordConsentToCompliance, referralCode, resolveCoupon, resolveEffectiveTheme, resolveLayout, resolveSection, revalidateDecision, secretsMatch, sectionDefinition, sectionId, sectionPath, setTheme, shortDeck, shortText, shouldServeLastKnown, storyDate, storyTowns, subscribeTheme, themeInitScript, townSlug, validCalendarDate, walnutCreekTimes, withLastKnown };