@happyvertical/smrt-content 0.37.2 → 0.37.3

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.
@@ -0,0 +1,587 @@
1
+ //#region src/content-governance.ts
2
+ var DEFAULT_FACT_RELATIONSHIP = "supports";
3
+ var DEFAULT_CONTENT_GOVERNANCE_CONFIG = {
4
+ policies: [{
5
+ key: "facts",
6
+ label: "Facts Review",
7
+ kind: "facts",
8
+ instructions: [
9
+ "Compare the draft copy against the supplied facts only.",
10
+ "Flag contradictions, unsupported claims, stale claims, and places where the copy should cite or qualify a statement.",
11
+ "Do not invent missing facts. If the draft makes a claim that is not supported by the provided facts, flag it clearly."
12
+ ].join(" "),
13
+ enabled: true
14
+ }, {
15
+ key: "safety",
16
+ label: "Safety Review",
17
+ kind: "safety",
18
+ instructions: [
19
+ "Review the content for legal, reputational, and user-safety risks.",
20
+ "At minimum, check for defamation risk, privacy leaks, unverified allegations, unsafe instructions, and medical, legal, or financial claims that need qualification.",
21
+ "Flag content that should be softened, attributed, removed, or escalated for human review."
22
+ ].join(" "),
23
+ enabled: true
24
+ }].map(clonePolicyDefinition),
25
+ profiles: [{
26
+ key: "publication",
27
+ label: "Publication",
28
+ description: "Default publication-time editorial checks.",
29
+ enabled: true,
30
+ requirements: [{
31
+ policyKey: "safety",
32
+ label: "Safety Review",
33
+ blocking: false
34
+ }, {
35
+ policyKey: "facts",
36
+ label: "Facts Review",
37
+ blocking: false
38
+ }]
39
+ }, {
40
+ key: "correction",
41
+ label: "Correction",
42
+ description: "Default correction-time editorial checks.",
43
+ enabled: true,
44
+ requirements: [{
45
+ policyKey: "safety",
46
+ label: "Safety Review",
47
+ blocking: false
48
+ }]
49
+ }].map(cloneProfileDefinition),
50
+ assignments: []
51
+ };
52
+ var governanceConfig = cloneGovernanceConfig(DEFAULT_CONTENT_GOVERNANCE_CONFIG);
53
+ function cloneReviewRequirement(requirement) {
54
+ return {
55
+ ...requirement,
56
+ acceptedStatuses: requirement.acceptedStatuses ? [...requirement.acceptedStatuses] : void 0
57
+ };
58
+ }
59
+ function normalizePolicyDefinition(policy) {
60
+ return {
61
+ key: policy.key,
62
+ label: policy.label || policy.key,
63
+ kind: policy.kind || getFallbackPolicyKind(policy.key),
64
+ instructions: policy.instructions || "",
65
+ enabled: policy.enabled !== false,
66
+ metadata: policy.metadata ? { ...policy.metadata } : void 0
67
+ };
68
+ }
69
+ function clonePolicyDefinition(policy) {
70
+ return normalizePolicyDefinition(policy);
71
+ }
72
+ function normalizeProfileDefinition(profile) {
73
+ return {
74
+ key: profile.key,
75
+ label: profile.label || profile.key,
76
+ description: profile.description || "",
77
+ enabled: profile.enabled !== false,
78
+ requirements: Array.isArray(profile.requirements) ? profile.requirements.map(cloneReviewRequirement) : [],
79
+ metadata: profile.metadata ? { ...profile.metadata } : void 0
80
+ };
81
+ }
82
+ function cloneProfileDefinition(profile) {
83
+ return normalizeProfileDefinition(profile);
84
+ }
85
+ function buildContentGovernanceAssignmentKey(contentType, contentVariant) {
86
+ return `${contentType || ""}::${contentVariant || ""}`;
87
+ }
88
+ function normalizeAssignmentDefinition(assignment) {
89
+ return {
90
+ key: assignment.key || buildContentGovernanceAssignmentKey(assignment.contentType, assignment.contentVariant),
91
+ label: assignment.label || "",
92
+ contentType: assignment.contentType,
93
+ contentVariant: assignment.contentVariant || "",
94
+ enabled: assignment.enabled !== false,
95
+ factLinkingEnabled: assignment.factLinkingEnabled === true,
96
+ transparencyEnabled: assignment.transparencyEnabled === true,
97
+ publicationProfileKey: assignment.publicationProfileKey || null,
98
+ correctionProfileKey: assignment.correctionProfileKey || null,
99
+ enforcePublishReadiness: assignment.enforcePublishReadiness === true,
100
+ defaultFactRelationship: assignment.defaultFactRelationship || DEFAULT_FACT_RELATIONSHIP,
101
+ metadata: assignment.metadata ? { ...assignment.metadata } : void 0
102
+ };
103
+ }
104
+ function cloneAssignmentDefinition(assignment) {
105
+ return normalizeAssignmentDefinition(assignment);
106
+ }
107
+ function cloneGovernanceConfig(config) {
108
+ return {
109
+ policies: config.policies.map(clonePolicyDefinition),
110
+ profiles: config.profiles.map(cloneProfileDefinition),
111
+ assignments: config.assignments.map(cloneAssignmentDefinition)
112
+ };
113
+ }
114
+ function mergeByKey(previous, next, normalize) {
115
+ const merged = /* @__PURE__ */ new Map();
116
+ for (const value of previous) {
117
+ const normalized = normalize(value);
118
+ if (normalized.key) merged.set(normalized.key, normalized);
119
+ }
120
+ for (const value of next) {
121
+ const normalized = normalize(value);
122
+ if (normalized.key) merged.set(normalized.key, normalized);
123
+ }
124
+ return [...merged.values()];
125
+ }
126
+ function getFallbackPolicyKind(key) {
127
+ if (key === "facts") return "facts";
128
+ if (key === "safety") return "safety";
129
+ return "custom";
130
+ }
131
+ function getPolicyMap(policies) {
132
+ return new Map(policies.map((policy) => {
133
+ const normalized = normalizePolicyDefinition(policy);
134
+ return [normalized.key, normalized];
135
+ }));
136
+ }
137
+ function getProfileMap(profiles) {
138
+ return new Map(profiles.map((profile) => {
139
+ const normalized = normalizeProfileDefinition(profile);
140
+ return [normalized.key, normalized];
141
+ }));
142
+ }
143
+ function isMissingGovernanceTableError(error) {
144
+ const message = error instanceof Error ? error.message : String(error || "Unknown error");
145
+ return message.includes("Run 'smrt db:migrate'") || /no such table/i.test(message) || /does not exist/i.test(message);
146
+ }
147
+ function getRowTimestamp(row, primaryKey) {
148
+ const snakeCaseKey = primaryKey === "createdAt" ? "created_at" : "updated_at";
149
+ const value = row[primaryKey] ?? row[snakeCaseKey];
150
+ return typeof value === "string" && value.length > 0 ? value : null;
151
+ }
152
+ function getRowTenantId(row) {
153
+ const value = row.tenantId ?? row.tenant_id ?? null;
154
+ return typeof value === "string" && value.length > 0 ? value : null;
155
+ }
156
+ function getRowString(row, ...keys) {
157
+ for (const key of keys) {
158
+ const value = row[key];
159
+ if (typeof value === "string" && value.length > 0) return value;
160
+ }
161
+ return null;
162
+ }
163
+ function safeParseJSONObject(value) {
164
+ if (!value) return {};
165
+ if (typeof value === "object" && !Array.isArray(value)) return { ...value };
166
+ try {
167
+ const parsed = JSON.parse(String(value));
168
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? { ...parsed } : {};
169
+ } catch {
170
+ return {};
171
+ }
172
+ }
173
+ function safeParseJSONArray(value, mapEntry) {
174
+ if (!value) return [];
175
+ if (Array.isArray(value)) return value.map((entry) => mapEntry(entry));
176
+ try {
177
+ const parsed = JSON.parse(String(value));
178
+ return Array.isArray(parsed) ? parsed.map((entry) => mapEntry(entry)) : [];
179
+ } catch {
180
+ return [];
181
+ }
182
+ }
183
+ function mapPersistedPolicyRow(row) {
184
+ return {
185
+ id: typeof row.id === "string" ? row.id : void 0,
186
+ tenantId: getRowTenantId(row),
187
+ createdAt: getRowTimestamp(row, "createdAt"),
188
+ updatedAt: getRowTimestamp(row, "updatedAt"),
189
+ ...normalizePolicyDefinition({
190
+ key: String(row.key || ""),
191
+ label: String(row.label || row.key || ""),
192
+ kind: row.kind || getFallbackPolicyKind(String(row.key || "")),
193
+ instructions: String(row.instructions || ""),
194
+ enabled: row.enabled !== false && row.enabled !== 0,
195
+ metadata: safeParseJSONObject(row.metadata)
196
+ })
197
+ };
198
+ }
199
+ function mapPersistedProfileRow(row) {
200
+ return {
201
+ id: typeof row.id === "string" ? row.id : void 0,
202
+ tenantId: getRowTenantId(row),
203
+ createdAt: getRowTimestamp(row, "createdAt"),
204
+ updatedAt: getRowTimestamp(row, "updatedAt"),
205
+ ...normalizeProfileDefinition({
206
+ key: String(row.key || ""),
207
+ label: String(row.label || row.key || ""),
208
+ description: String(row.description || ""),
209
+ enabled: row.enabled !== false && row.enabled !== 0,
210
+ requirements: safeParseJSONArray(row.requirements, cloneReviewRequirement),
211
+ metadata: safeParseJSONObject(row.metadata)
212
+ })
213
+ };
214
+ }
215
+ function mapPersistedAssignmentRow(row) {
216
+ return {
217
+ id: typeof row.id === "string" ? row.id : void 0,
218
+ tenantId: getRowTenantId(row),
219
+ createdAt: getRowTimestamp(row, "createdAt"),
220
+ updatedAt: getRowTimestamp(row, "updatedAt"),
221
+ ...normalizeAssignmentDefinition({
222
+ key: String(row.key || ""),
223
+ label: String(row.label || ""),
224
+ contentType: String(row.contentType || row.content_type || ""),
225
+ contentVariant: String(row.contentVariant || row.content_variant || ""),
226
+ enabled: row.enabled !== false && row.enabled !== 0,
227
+ factLinkingEnabled: row.factLinkingEnabled === true || row.fact_linking_enabled === true || row.fact_linking_enabled === 1,
228
+ transparencyEnabled: row.transparencyEnabled === true || row.transparency_enabled === true || row.transparency_enabled === 1,
229
+ publicationProfileKey: getRowString(row, "publicationProfileKey", "publication_profile_key"),
230
+ correctionProfileKey: getRowString(row, "correctionProfileKey", "correction_profile_key"),
231
+ enforcePublishReadiness: row.enforcePublishReadiness === true || row.enforce_publish_readiness === true || row.enforce_publish_readiness === 1,
232
+ defaultFactRelationship: getRowString(row, "defaultFactRelationship", "default_fact_relationship") || DEFAULT_FACT_RELATIONSHIP,
233
+ metadata: safeParseJSONObject(row.metadata)
234
+ })
235
+ };
236
+ }
237
+ async function loadPersistedContentGovernanceDefinitions(options = {}) {
238
+ const { db } = options;
239
+ if (!db) return {
240
+ policies: [],
241
+ profiles: [],
242
+ assignments: []
243
+ };
244
+ try {
245
+ const [policyRows, profileRows, assignmentRows] = await Promise.all([
246
+ db.list("content_governance_policies", {}),
247
+ db.list("content_governance_profiles", {}),
248
+ db.list("content_governance_assignments", {})
249
+ ]);
250
+ const byCreatedAt = (a, b) => String(a.created_at || a.createdAt || "").localeCompare(String(b.created_at || b.createdAt || ""));
251
+ policyRows.sort(byCreatedAt);
252
+ profileRows.sort(byCreatedAt);
253
+ assignmentRows.sort(byCreatedAt);
254
+ return {
255
+ policies: policyRows.map((row) => mapPersistedPolicyRow(row)),
256
+ profiles: profileRows.map((row) => mapPersistedProfileRow(row)),
257
+ assignments: assignmentRows.map((row) => mapPersistedAssignmentRow(row))
258
+ };
259
+ } catch (error) {
260
+ if (isMissingGovernanceTableError(error)) return {
261
+ policies: [],
262
+ profiles: [],
263
+ assignments: []
264
+ };
265
+ throw error;
266
+ }
267
+ }
268
+ function resolveAssignmentDefinition(assignments, options) {
269
+ if (!options.contentType) return null;
270
+ const exactMatch = assignments.find((assignment) => assignment.contentType === options.contentType && (assignment.contentVariant || "") === (options.contentVariant || "")) || null;
271
+ if (exactMatch) return cloneAssignmentDefinition(exactMatch);
272
+ const typeOnlyMatch = assignments.find((assignment) => assignment.contentType === options.contentType && !assignment.contentVariant) || null;
273
+ return typeOnlyMatch ? cloneAssignmentDefinition(typeOnlyMatch) : null;
274
+ }
275
+ function buildResolvedGovernance(config, assignment) {
276
+ const normalizedAssignment = assignment ? normalizeAssignmentDefinition(assignment) : null;
277
+ if (!normalizedAssignment || normalizedAssignment.enabled !== true) return {
278
+ isGoverned: false,
279
+ factLinkingEnabled: false,
280
+ transparencyEnabled: false,
281
+ publicationProfileKey: null,
282
+ correctionProfileKey: null,
283
+ enforcePublishReadiness: false,
284
+ defaultFactRelationship: DEFAULT_FACT_RELATIONSHIP,
285
+ reviewPolicies: config.policies.map(clonePolicyDefinition).filter((policy) => policy.enabled !== false),
286
+ availableProfiles: config.profiles.map(cloneProfileDefinition).filter((profile) => profile.enabled !== false),
287
+ assignment: normalizedAssignment
288
+ };
289
+ return {
290
+ isGoverned: true,
291
+ factLinkingEnabled: normalizedAssignment.factLinkingEnabled === true,
292
+ transparencyEnabled: normalizedAssignment.transparencyEnabled === true,
293
+ publicationProfileKey: normalizedAssignment.publicationProfileKey || null,
294
+ correctionProfileKey: normalizedAssignment.correctionProfileKey || null,
295
+ enforcePublishReadiness: normalizedAssignment.enforcePublishReadiness === true,
296
+ defaultFactRelationship: normalizedAssignment.defaultFactRelationship || DEFAULT_FACT_RELATIONSHIP,
297
+ reviewPolicies: config.policies.map(clonePolicyDefinition).filter((policy) => policy.enabled !== false),
298
+ availableProfiles: config.profiles.map(cloneProfileDefinition).filter((profile) => profile.enabled !== false),
299
+ assignment: normalizedAssignment
300
+ };
301
+ }
302
+ function normalizeStatus(status) {
303
+ switch (status) {
304
+ case "pending":
305
+ case "passed":
306
+ case "flagged":
307
+ case "failed":
308
+ case "waived": return status;
309
+ default: return "flagged";
310
+ }
311
+ }
312
+ function normalizeSeverity(severity) {
313
+ switch (severity) {
314
+ case "info":
315
+ case "warning":
316
+ case "error": return severity;
317
+ default: return "warning";
318
+ }
319
+ }
320
+ function extractJSONObject(raw) {
321
+ const start = raw.indexOf("{");
322
+ const end = raw.lastIndexOf("}");
323
+ if (start === -1 || end === -1 || end <= start) return null;
324
+ return raw.slice(start, end + 1);
325
+ }
326
+ function getContentGovernanceConfig() {
327
+ return cloneGovernanceConfig(governanceConfig);
328
+ }
329
+ function configureContentGovernance(config) {
330
+ governanceConfig = {
331
+ policies: config.policies ? mergeByKey(governanceConfig.policies, config.policies, normalizePolicyDefinition) : governanceConfig.policies.map(clonePolicyDefinition),
332
+ profiles: config.profiles ? mergeByKey(governanceConfig.profiles, config.profiles, normalizeProfileDefinition) : governanceConfig.profiles.map(cloneProfileDefinition),
333
+ assignments: config.assignments ? mergeByKey(governanceConfig.assignments, config.assignments, normalizeAssignmentDefinition) : governanceConfig.assignments.map(cloneAssignmentDefinition)
334
+ };
335
+ return getContentGovernanceConfig();
336
+ }
337
+ function resetContentGovernanceConfig() {
338
+ governanceConfig = cloneGovernanceConfig(DEFAULT_CONTENT_GOVERNANCE_CONFIG);
339
+ return getContentGovernanceConfig();
340
+ }
341
+ async function getEffectiveContentGovernanceConfig(options = {}) {
342
+ const persisted = await loadPersistedContentGovernanceDefinitions({ db: options.db });
343
+ return {
344
+ policies: mergeByKey(governanceConfig.policies, persisted.policies, normalizePolicyDefinition),
345
+ profiles: mergeByKey(governanceConfig.profiles, persisted.profiles, normalizeProfileDefinition),
346
+ assignments: mergeByKey(governanceConfig.assignments, persisted.assignments, normalizeAssignmentDefinition)
347
+ };
348
+ }
349
+ function hasStaticContentGovernancePolicy(key) {
350
+ return getPolicyMap(governanceConfig.policies).has(key);
351
+ }
352
+ function hasStaticContentGovernanceProfile(key) {
353
+ return getProfileMap(governanceConfig.profiles).has(key);
354
+ }
355
+ function getContentReviewPolicy(policyKey, policies = governanceConfig.policies) {
356
+ return getPolicyMap(policies).get(policyKey) || null;
357
+ }
358
+ function getContentReviewKind(policyKey, policies = governanceConfig.policies) {
359
+ return getPolicyMap(policies).get(policyKey)?.kind || getFallbackPolicyKind(policyKey);
360
+ }
361
+ function getContentReviewProfile(profileKey, profiles = governanceConfig.profiles) {
362
+ return getProfileMap(profiles).get(profileKey) || null;
363
+ }
364
+ function getContentReviewProfileKeys(profiles = governanceConfig.profiles) {
365
+ return profiles.map(cloneProfileDefinition).filter((profile) => profile.enabled !== false).map((profile) => profile.key);
366
+ }
367
+ function getContentReviewPolicies(policies = governanceConfig.policies) {
368
+ return policies.map(clonePolicyDefinition).filter((policy) => policy.enabled !== false);
369
+ }
370
+ function getContentReviewRequirements(profileKey, profiles = governanceConfig.profiles) {
371
+ return getContentReviewProfile(profileKey, profiles)?.requirements.map(cloneReviewRequirement) || [];
372
+ }
373
+ function getAcceptedContentReviewStatuses(requirement) {
374
+ return requirement.acceptedStatuses && requirement.acceptedStatuses.length > 0 ? [...requirement.acceptedStatuses] : ["passed", "waived"];
375
+ }
376
+ function resolveConfiguredContentGovernance(options) {
377
+ const assignment = resolveAssignmentDefinition(governanceConfig.assignments, {
378
+ contentType: options.contentType,
379
+ contentVariant: options.contentVariant
380
+ });
381
+ return buildResolvedGovernance(governanceConfig, assignment);
382
+ }
383
+ async function resolveEffectiveContentGovernance(options) {
384
+ const effectiveConfig = await getEffectiveContentGovernanceConfig({ db: options.db });
385
+ return buildResolvedGovernance(effectiveConfig, resolveAssignmentDefinition(effectiveConfig.assignments, {
386
+ contentType: options.contentType,
387
+ contentVariant: options.contentVariant
388
+ }));
389
+ }
390
+ function buildContentReviewPrompt(options) {
391
+ const { kind, content, facts = [], policy, customInstructions } = options;
392
+ const factLines = facts.length > 0 ? facts.map((fact) => `- [${fact.id}] status=${fact.status}; confidence=${fact.confidence}; sources=${fact.sourceCount}; text=${fact.textRefined}`).join("\n") : "No facts were supplied for this review.";
393
+ const policyText = customInstructions?.trim() || policy?.instructions || getContentReviewPolicy(kind)?.instructions || "";
394
+ return `You are a structured editorial reviewer.
395
+
396
+ Return ONLY valid JSON with this shape:
397
+ {
398
+ "status": "passed" | "flagged" | "failed" | "waived",
399
+ "summary": "short summary",
400
+ "findings": [
401
+ {
402
+ "severity": "info" | "warning" | "error",
403
+ "title": "short title",
404
+ "detail": "what is wrong and why",
405
+ "factId": "optional fact id",
406
+ "quote": "optional quoted text from the draft",
407
+ "suggestedChange": "optional suggested fix",
408
+ "ruleId": "optional policy or rule id"
409
+ }
410
+ ]
411
+ }
412
+
413
+ Review kind: ${kind}
414
+ Policy key: ${policy?.key || kind}
415
+ Review instructions:
416
+ ${policyText}
417
+
418
+ Draft content:
419
+ - id: ${content.id ?? ""}
420
+ - type: ${content.type ?? ""}
421
+ - status: ${content.status}
422
+ - state: ${content.state}
423
+ - author: ${content.author ?? ""}
424
+ - publish_date: ${content.publish_date?.toISOString?.() ?? ""}
425
+
426
+ Title:
427
+ ${content.title}
428
+
429
+ Description:
430
+ ${content.description ?? ""}
431
+
432
+ Body:
433
+ ${content.body}
434
+
435
+ Relevant facts:
436
+ ${factLines}`;
437
+ }
438
+ function parseContentReviewResponse(raw) {
439
+ const normalizedRaw = raw.trim();
440
+ const jsonCandidate = extractJSONObject(normalizedRaw);
441
+ if (jsonCandidate) try {
442
+ const parsed = JSON.parse(jsonCandidate);
443
+ const findings = Array.isArray(parsed.findings) ? parsed.findings.map((rawFinding) => {
444
+ const finding = rawFinding && typeof rawFinding === "object" ? rawFinding : {};
445
+ return {
446
+ severity: normalizeSeverity(finding.severity),
447
+ title: String(finding.title || "Review finding"),
448
+ detail: String(finding.detail || ""),
449
+ factId: typeof finding.factId === "string" ? finding.factId : void 0,
450
+ quote: typeof finding.quote === "string" ? finding.quote : void 0,
451
+ suggestedChange: typeof finding.suggestedChange === "string" ? finding.suggestedChange : void 0,
452
+ ruleId: typeof finding.ruleId === "string" ? finding.ruleId : void 0
453
+ };
454
+ }) : [];
455
+ return {
456
+ status: normalizeStatus(parsed.status),
457
+ summary: String(parsed.summary || normalizedRaw || "Review completed"),
458
+ findings
459
+ };
460
+ } catch {}
461
+ return {
462
+ status: "flagged",
463
+ summary: normalizedRaw || "Review completed without structured output.",
464
+ findings: normalizedRaw ? [{
465
+ severity: "warning",
466
+ title: "Unstructured review output",
467
+ detail: normalizedRaw
468
+ }] : []
469
+ };
470
+ }
471
+ //#endregion
472
+ //#region src/content-transparency.ts
473
+ function asObject(value, fallback = {}) {
474
+ return value && typeof value === "object" ? { ...value } : fallback;
475
+ }
476
+ function asString(value) {
477
+ return typeof value === "string" && value.length > 0 ? value : null;
478
+ }
479
+ function asNumber(value) {
480
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
481
+ }
482
+ function asArray(value) {
483
+ return Array.isArray(value) ? value : [];
484
+ }
485
+ function normalizeGeneration(value) {
486
+ const generation = asObject(value);
487
+ return {
488
+ aiAssisted: Boolean(generation.aiAssisted),
489
+ publicPrompt: asString(generation.publicPrompt),
490
+ model: asString(generation.model)
491
+ };
492
+ }
493
+ function normalizeFact(value) {
494
+ const fact = asObject(value);
495
+ return {
496
+ ...fact,
497
+ id: asString(fact.id),
498
+ relationship: asString(fact.relationship),
499
+ linkMetadata: asObject(fact.linkMetadata),
500
+ usedInArticle: Boolean(fact.usedInArticle),
501
+ sources: asArray(fact.sources).map(normalizeSource)
502
+ };
503
+ }
504
+ function normalizeSource(value) {
505
+ const source = asObject(value);
506
+ return {
507
+ id: asString(source.id),
508
+ sourceType: asString(source.sourceType),
509
+ sourceUrl: asString(source.sourceUrl),
510
+ sourceTitle: asString(source.sourceTitle),
511
+ credibility: asNumber(source.credibility),
512
+ extractedAt: asString(source.extractedAt),
513
+ metadata: asObject(source.metadata)
514
+ };
515
+ }
516
+ function normalizeReference(value) {
517
+ const reference = asObject(value);
518
+ return {
519
+ id: asString(reference.id),
520
+ title: asString(reference.title),
521
+ url: asString(reference.url),
522
+ originalUrl: asString(reference.originalUrl),
523
+ type: asString(reference.type),
524
+ source: asString(reference.source),
525
+ usedFactIds: asArray(reference.usedFactIds).filter(Boolean),
526
+ extractedFacts: asArray(reference.extractedFacts).map(normalizeFact)
527
+ };
528
+ }
529
+ function normalizePublicationVersion(value) {
530
+ const publicationVersion = asObject(value);
531
+ if (!publicationVersion.id && publicationVersion.version === void 0) return null;
532
+ return {
533
+ id: asString(publicationVersion.id),
534
+ version: asNumber(publicationVersion.version),
535
+ kind: asString(publicationVersion.kind),
536
+ summary: typeof publicationVersion.summary === "string" ? publicationVersion.summary : "",
537
+ createdAt: asString(publicationVersion.createdAt)
538
+ };
539
+ }
540
+ function normalizeVersionHistoryItem(value) {
541
+ const version = asObject(value);
542
+ return {
543
+ id: asString(version.id),
544
+ version: asNumber(version.version),
545
+ kind: asString(version.kind),
546
+ summary: typeof version.summary === "string" ? version.summary : "",
547
+ createdAt: asString(version.createdAt),
548
+ provenance: asObject(version.provenance)
549
+ };
550
+ }
551
+ function dedupeFacts(facts) {
552
+ const byKey = /* @__PURE__ */ new Map();
553
+ for (const fact of facts) {
554
+ const key = fact.id || fact.textRefined || fact.textRaw || JSON.stringify(fact.metadata || {});
555
+ if (!key) continue;
556
+ byKey.set(key, fact);
557
+ }
558
+ return [...byKey.values()];
559
+ }
560
+ function normalizeContentTransparency(value, defaults = {}) {
561
+ const snapshot = asObject(value);
562
+ const references = asArray(snapshot.references).map(normalizeReference);
563
+ const linkedFacts = asArray(snapshot.linkedFacts).map(normalizeFact);
564
+ const factsUsed = asArray(snapshot.factsUsed).length > 0 ? asArray(snapshot.factsUsed).map(normalizeFact) : linkedFacts.filter((fact) => fact.usedInArticle);
565
+ const otherExtractedFacts = asArray(snapshot.otherExtractedFacts).length > 0 ? asArray(snapshot.otherExtractedFacts).map(normalizeFact) : dedupeFacts(references.flatMap((reference) => reference.extractedFacts.filter((fact) => !fact.usedInArticle)));
566
+ return {
567
+ generatedAt: asString(snapshot.generatedAt) ?? defaults.generatedAt ?? null,
568
+ snapshotKind: snapshot.snapshotKind === "published" ? "published" : defaults.snapshotKind || "preview",
569
+ contentId: asString(snapshot.contentId) ?? defaults.contentId ?? null,
570
+ currentContentStatus: asString(snapshot.currentContentStatus) ?? defaults.currentContentStatus ?? null,
571
+ publicationProfileKey: asString(snapshot.publicationProfileKey) ?? asString(snapshot.publicationReviewProfileKey) ?? defaults.publicationProfileKey ?? "publication",
572
+ publicationVersion: normalizePublicationVersion(snapshot.publicationVersion) ?? defaults.publicationVersion ?? null,
573
+ generation: normalizeGeneration(snapshot.generation ?? defaults.generation ?? {}),
574
+ factsUsed,
575
+ linkedFacts,
576
+ otherExtractedFacts,
577
+ references,
578
+ reviews: asArray(snapshot.reviews),
579
+ reviewProfiles: asArray(snapshot.reviewProfiles),
580
+ corrections: asArray(snapshot.corrections),
581
+ versionHistory: asArray(snapshot.versionHistory).map(normalizeVersionHistoryItem)
582
+ };
583
+ }
584
+ //#endregion
585
+ export { loadPersistedContentGovernanceDefinitions as _, getAcceptedContentReviewStatuses as a, resolveConfiguredContentGovernance as b, getContentReviewPolicies as c, getContentReviewProfileKeys as d, getContentReviewRequirements as f, hasStaticContentGovernanceProfile as g, hasStaticContentGovernancePolicy as h, configureContentGovernance as i, getContentReviewPolicy as l, getFallbackPolicyKind as m, buildContentGovernanceAssignmentKey as n, getContentGovernanceConfig as o, getEffectiveContentGovernanceConfig as p, buildContentReviewPrompt as r, getContentReviewKind as s, normalizeContentTransparency as t, getContentReviewProfile as u, parseContentReviewResponse as v, resolveEffectiveContentGovernance as x, resetContentGovernanceConfig as y };
586
+
587
+ //# sourceMappingURL=content-transparency-OvUNs-bU.js.map