@scalemule/news-publication 0.0.22 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,227 +0,0 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
-
3
- // src/verified-card.tsx
4
- var ALLOWED_CARD_TYPES = /* @__PURE__ */ new Set([
5
- "PLACE",
6
- "ACTIVITY",
7
- "EVENT",
8
- "ORGANIZATION",
9
- "PRODUCT"
10
- ]);
11
- function clip(val, maxChars) {
12
- if (typeof val !== "string") return void 0;
13
- const trimmed = val.trim();
14
- if (!trimmed) return void 0;
15
- return Array.from(trimmed).slice(0, maxChars).join("");
16
- }
17
- function isSafeActionUrl(raw) {
18
- if (typeof raw !== "string") return false;
19
- const trimmed = raw.trim();
20
- if (!trimmed || trimmed.length > 2048) return false;
21
- if (trimmed.startsWith("tel:") || trimmed.startsWith("mailto:")) {
22
- return /^tel:\+?[\d\-().\s]+$/.test(trimmed) || /^mailto:[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed);
23
- }
24
- try {
25
- const url = new URL(trimmed);
26
- if (url.protocol !== "https:" && url.protocol !== "http:") return false;
27
- if (url.username || url.password) return false;
28
- const hostname = url.hostname.toLowerCase();
29
- if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".internal") || hostname.endsWith(".local") || hostname === "127.0.0.1" || hostname === "169.254.169.254") {
30
- return false;
31
- }
32
- for (const [key] of url.searchParams.entries()) {
33
- const cleanKey = key.toLowerCase().replace(/[^a-z0-9]/g, "");
34
- if (cleanKey === "token" || cleanKey === "apikey" || cleanKey === "accesstoken" || cleanKey === "secret" || cleanKey === "clientsecret" || cleanKey === "authorization" || cleanKey === "signature" || cleanKey === "password" || cleanKey === "xamzsignature" || cleanKey === "xamzsecuritytoken") {
35
- return false;
36
- }
37
- }
38
- return true;
39
- } catch {
40
- return false;
41
- }
42
- }
43
- function getArticleCards(rawCards) {
44
- if (!Array.isArray(rawCards) || rawCards.length === 0) return [];
45
- const cards = [];
46
- for (const item of rawCards) {
47
- if (!item || typeof item !== "object") continue;
48
- const c = item;
49
- const id = clip(c.id, 64);
50
- const title = clip(c.title, 300);
51
- if (!id || !title) continue;
52
- const rawType = typeof c.card_type === "string" ? c.card_type : c.cardType;
53
- const cardType = ALLOWED_CARD_TYPES.has(rawType) ? rawType : "PLACE";
54
- const kicker = clip(c.kicker, 120) || (cardType === "PLACE" ? "LOCAL VENUE \xB7 VERIFIED DIRECTORY" : cardType === "EVENT" ? "COMMUNITY EVENT \xB7 VERIFIED FACTSHEET" : cardType === "ACTIVITY" ? "COMMUNITY ACTIVITY \xB7 VERIFIED FACTSHEET" : cardType === "ORGANIZATION" ? "COMMUNITY ORGANIZATION \xB7 DIRECTORY" : "VERIFIED FACTSHEET");
55
- const subtitle = clip(c.subtitle, 300);
56
- const summary = clip(c.summary, 1e3);
57
- const badge = clip(c.badge, 64);
58
- const facts = [];
59
- if (Array.isArray(c.facts)) {
60
- for (const f of c.facts) {
61
- if (!f || typeof f !== "object") continue;
62
- const label = clip(f.label, 80);
63
- const value = clip(f.value, 500);
64
- if (!label || !value) continue;
65
- let href = void 0;
66
- const rawHref = f.href;
67
- if (typeof rawHref === "string" && isSafeActionUrl(rawHref)) {
68
- href = rawHref;
69
- }
70
- facts.push({ label, value, href });
71
- }
72
- } else {
73
- const timeframe = clip(c.timeframe, 200);
74
- if (timeframe) facts.push({ label: "Timeframe", value: timeframe });
75
- const address = clip(c.address, 300);
76
- if (address) {
77
- facts.push({
78
- label: "Address",
79
- value: address,
80
- href: `https://maps.google.com/?q=${encodeURIComponent(address)}`
81
- });
82
- }
83
- const hours = clip(c.hours, 300);
84
- if (hours) facts.push({ label: "Hours", value: hours });
85
- const phone = clip(c.phone, 80);
86
- if (phone) {
87
- facts.push({
88
- label: "Phone",
89
- value: phone,
90
- href: `tel:${phone.replace(/[^+\d]/g, "")}`
91
- });
92
- }
93
- const cost = clip(c.cost, 120);
94
- if (cost) facts.push({ label: "Cost", value: cost });
95
- }
96
- const actions = [];
97
- if (Array.isArray(c.actions)) {
98
- for (const act of c.actions) {
99
- if (!act || typeof act !== "object") continue;
100
- const actObj = act;
101
- const label = clip(actObj.label, 80);
102
- const url = typeof actObj.url === "string" ? actObj.url.trim() : "";
103
- if (!label || !url || !isSafeActionUrl(url)) continue;
104
- actions.push({
105
- label,
106
- url,
107
- primary: Boolean(actObj.primary)
108
- });
109
- if (actions.length >= 5) break;
110
- }
111
- }
112
- let image = void 0;
113
- if (c.image && typeof c.image === "object") {
114
- const img = c.image;
115
- const masterUrl = typeof img.master_url === "string" && isSafeActionUrl(img.master_url) ? img.master_url : void 0;
116
- const url = typeof img.url === "string" && isSafeActionUrl(img.url) ? img.url : masterUrl;
117
- if (url) {
118
- image = {
119
- file_id: clip(img.file_id, 64),
120
- master_url: masterUrl,
121
- url,
122
- width: typeof img.width === "number" ? img.width : void 0,
123
- height: typeof img.height === "number" ? img.height : void 0,
124
- alt: clip(img.alt, 500),
125
- caption: clip(img.caption, 1e3),
126
- attribution: clip(img.attribution, 500),
127
- label: clip(img.label, 100),
128
- ai_generated: Boolean(img.ai_generated)
129
- };
130
- }
131
- }
132
- const verificationNote = clip(c.verification_note || c.verificationNote, 300) || "Verified by publication reporting staff";
133
- const verifiedAt = clip(c.verified_at || c.verifiedAt, 100) || "October 2026";
134
- let rating = void 0;
135
- if (typeof c.rating === "number" || typeof c.rating === "string") {
136
- rating = c.rating;
137
- }
138
- cards.push({
139
- id,
140
- cardType,
141
- kicker,
142
- title,
143
- subtitle,
144
- summary,
145
- badge,
146
- facts,
147
- actions,
148
- verificationNote,
149
- verifiedAt,
150
- image,
151
- rating
152
- });
153
- if (cards.length >= 10) break;
154
- }
155
- return cards;
156
- }
157
- function VerifiedCardComponent({
158
- card,
159
- className
160
- }) {
161
- return /* @__PURE__ */ jsxs(
162
- "aside",
163
- {
164
- className: `article-verified-card ${className || ""}`.trim(),
165
- "aria-label": `Verified Factsheet: ${card.title}`,
166
- children: [
167
- /* @__PURE__ */ jsxs("div", { className: "article-verified-card__header", children: [
168
- /* @__PURE__ */ jsx("span", { className: "article-verified-card__kicker", children: card.kicker }),
169
- card.badge && /* @__PURE__ */ jsx("span", { className: "article-verified-card__badge", children: card.badge })
170
- ] }),
171
- /* @__PURE__ */ jsx("h2", { className: "article-verified-card__title", children: card.title }),
172
- card.subtitle && /* @__PURE__ */ jsx("p", { className: "article-verified-card__subtitle", children: card.subtitle }),
173
- card.summary && /* @__PURE__ */ jsx("p", { className: "article-verified-card__summary", children: card.summary }),
174
- card.facts.length > 0 && /* @__PURE__ */ jsx("dl", { className: "article-verified-card__fact-grid", children: card.facts.map((fact, i) => /* @__PURE__ */ jsxs("div", { className: "article-verified-card__fact-row", children: [
175
- /* @__PURE__ */ jsx("dt", { className: "article-verified-card__fact-label", children: fact.label }),
176
- /* @__PURE__ */ jsx("dd", { className: "article-verified-card__fact-value", children: fact.href ? /* @__PURE__ */ jsx(
177
- "a",
178
- {
179
- className: "article-verified-card__fact-link",
180
- href: fact.href,
181
- rel: "noopener noreferrer",
182
- target: fact.href.startsWith("http") ? "_blank" : void 0,
183
- children: fact.value
184
- }
185
- ) : fact.value })
186
- ] }, i)) }),
187
- card.actions.length > 0 && /* @__PURE__ */ jsx("div", { className: "article-verified-card__actions", children: card.actions.map((act, i) => /* @__PURE__ */ jsxs(
188
- "a",
189
- {
190
- className: `article-verified-card__action ${act.primary ? "article-verified-card__action--primary" : "article-verified-card__action--secondary"}`,
191
- href: act.url,
192
- target: "_blank",
193
- rel: "noopener noreferrer",
194
- children: [
195
- /* @__PURE__ */ jsx("span", { children: act.label }),
196
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "article-verified-card__action-icon", children: "\u2197" })
197
- ]
198
- },
199
- i
200
- )) }),
201
- /* @__PURE__ */ jsxs("footer", { className: "article-verified-card__footer", children: [
202
- /* @__PURE__ */ jsxs("div", { className: "article-verified-card__seal", children: [
203
- /* @__PURE__ */ jsx("span", { className: "article-verified-card__seal-check", "aria-hidden": "true", children: "\u2713" }),
204
- /* @__PURE__ */ jsx("span", { children: card.verificationNote })
205
- ] }),
206
- /* @__PURE__ */ jsx("span", { className: "article-verified-card__date", children: card.verifiedAt })
207
- ] })
208
- ]
209
- }
210
- );
211
- }
212
- function VerifiedCardSection({
213
- cards,
214
- className
215
- }) {
216
- if (!cards || cards.length === 0) return null;
217
- return /* @__PURE__ */ jsx(
218
- "section",
219
- {
220
- className: `article-verified-card-section ${className || ""}`.trim(),
221
- "aria-label": "Verified Community Information",
222
- children: cards.map((card) => /* @__PURE__ */ jsx(VerifiedCardComponent, { card }, card.id))
223
- }
224
- );
225
- }
226
-
227
- export { VerifiedCardComponent, VerifiedCardSection, getArticleCards, isSafeActionUrl };