@actuate-media/cms-core 0.76.3 → 0.77.0

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 (79) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/__tests__/ai/brand-voice-alignment.test.d.ts +2 -0
  3. package/dist/__tests__/ai/brand-voice-alignment.test.d.ts.map +1 -0
  4. package/dist/__tests__/ai/brand-voice-alignment.test.js +38 -0
  5. package/dist/__tests__/ai/brand-voice-alignment.test.js.map +1 -0
  6. package/dist/__tests__/ai/brand-voice-analyze.test.d.ts +2 -0
  7. package/dist/__tests__/ai/brand-voice-analyze.test.d.ts.map +1 -0
  8. package/dist/__tests__/ai/brand-voice-analyze.test.js +117 -0
  9. package/dist/__tests__/ai/brand-voice-analyze.test.js.map +1 -0
  10. package/dist/__tests__/ai/brand-voice-document.test.d.ts +2 -0
  11. package/dist/__tests__/ai/brand-voice-document.test.d.ts.map +1 -0
  12. package/dist/__tests__/ai/brand-voice-document.test.js +74 -0
  13. package/dist/__tests__/ai/brand-voice-document.test.js.map +1 -0
  14. package/dist/__tests__/ai/brand-voice-fix.test.d.ts +2 -0
  15. package/dist/__tests__/ai/brand-voice-fix.test.d.ts.map +1 -0
  16. package/dist/__tests__/ai/brand-voice-fix.test.js +34 -0
  17. package/dist/__tests__/ai/brand-voice-fix.test.js.map +1 -0
  18. package/dist/__tests__/ai/brand-voice-sources.test.d.ts +2 -0
  19. package/dist/__tests__/ai/brand-voice-sources.test.d.ts.map +1 -0
  20. package/dist/__tests__/ai/brand-voice-sources.test.js +14 -0
  21. package/dist/__tests__/ai/brand-voice-sources.test.js.map +1 -0
  22. package/dist/__tests__/ai/brand-voice-structured.test.d.ts +2 -0
  23. package/dist/__tests__/ai/brand-voice-structured.test.d.ts.map +1 -0
  24. package/dist/__tests__/ai/brand-voice-structured.test.js +49 -0
  25. package/dist/__tests__/ai/brand-voice-structured.test.js.map +1 -0
  26. package/dist/__tests__/api/route-contracts.test.js +3 -0
  27. package/dist/__tests__/api/route-contracts.test.js.map +1 -1
  28. package/dist/ai/brand-voice-alignment.d.ts +17 -0
  29. package/dist/ai/brand-voice-alignment.d.ts.map +1 -0
  30. package/dist/ai/brand-voice-alignment.js +40 -0
  31. package/dist/ai/brand-voice-alignment.js.map +1 -0
  32. package/dist/ai/brand-voice-analyze.d.ts +71 -0
  33. package/dist/ai/brand-voice-analyze.d.ts.map +1 -0
  34. package/dist/ai/brand-voice-analyze.js +151 -0
  35. package/dist/ai/brand-voice-analyze.js.map +1 -0
  36. package/dist/ai/brand-voice-document.d.ts +56 -0
  37. package/dist/ai/brand-voice-document.d.ts.map +1 -0
  38. package/dist/ai/brand-voice-document.js +335 -0
  39. package/dist/ai/brand-voice-document.js.map +1 -0
  40. package/dist/ai/brand-voice-fix.d.ts +26 -0
  41. package/dist/ai/brand-voice-fix.d.ts.map +1 -0
  42. package/dist/ai/brand-voice-fix.js +97 -0
  43. package/dist/ai/brand-voice-fix.js.map +1 -0
  44. package/dist/ai/brand-voice-save.d.ts +11 -0
  45. package/dist/ai/brand-voice-save.d.ts.map +1 -0
  46. package/dist/ai/brand-voice-save.js +51 -0
  47. package/dist/ai/brand-voice-save.js.map +1 -0
  48. package/dist/ai/brand-voice-sources.d.ts +16 -0
  49. package/dist/ai/brand-voice-sources.d.ts.map +1 -0
  50. package/dist/ai/brand-voice-sources.js +106 -0
  51. package/dist/ai/brand-voice-sources.js.map +1 -0
  52. package/dist/ai/brand-voice-structured.d.ts +87 -0
  53. package/dist/ai/brand-voice-structured.d.ts.map +1 -0
  54. package/dist/ai/brand-voice-structured.js +438 -0
  55. package/dist/ai/brand-voice-structured.js.map +1 -0
  56. package/dist/ai/brand-voice.d.ts +3 -0
  57. package/dist/ai/brand-voice.d.ts.map +1 -1
  58. package/dist/ai/brand-voice.js +18 -0
  59. package/dist/ai/brand-voice.js.map +1 -1
  60. package/dist/ai/index.d.ts +7 -0
  61. package/dist/ai/index.d.ts.map +1 -1
  62. package/dist/ai/index.js +7 -0
  63. package/dist/ai/index.js.map +1 -1
  64. package/dist/ai/types.d.ts +8 -0
  65. package/dist/ai/types.d.ts.map +1 -1
  66. package/dist/api/routes/ai-seo.d.ts.map +1 -1
  67. package/dist/api/routes/ai-seo.js +22 -3
  68. package/dist/api/routes/ai-seo.js.map +1 -1
  69. package/dist/api/routes/ai-settings.d.ts.map +1 -1
  70. package/dist/api/routes/ai-settings.js +272 -7
  71. package/dist/api/routes/ai-settings.js.map +1 -1
  72. package/dist/api/routes/saved-sections.d.ts.map +1 -1
  73. package/dist/api/routes/saved-sections.js +4 -1
  74. package/dist/api/routes/saved-sections.js.map +1 -1
  75. package/dist/brand-voice/index.d.ts +7 -0
  76. package/dist/brand-voice/index.d.ts.map +1 -0
  77. package/dist/brand-voice/index.js +7 -0
  78. package/dist/brand-voice/index.js.map +1 -0
  79. package/package.json +6 -1
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Hybrid brand-voice fixer — deterministic rule fixes first, optional AI rewrite
3
+ * for remaining alignment issues.
4
+ */
5
+ import { type BrandAlignmentIssue, type BrandAlignmentScoreResult, type BrandVoiceStructuredProfile } from './brand-voice-structured.js';
6
+ export interface DeterministicFixResult {
7
+ text: string;
8
+ fixes: string[];
9
+ }
10
+ /** Apply safe, deterministic fixes from structured rules (no LLM). */
11
+ export declare function applyDeterministicBrandFixes(content: string, structured: BrandVoiceStructuredProfile, channelId?: string): DeterministicFixResult;
12
+ export declare function buildBrandVoiceFixPrompt(content: string, issues: BrandAlignmentIssue[], structured: BrandVoiceStructuredProfile): {
13
+ system: string;
14
+ prompt: string;
15
+ };
16
+ export interface BrandVoiceFixOutcome {
17
+ text: string;
18
+ deterministicFixes: string[];
19
+ beforeScore: BrandAlignmentScoreResult;
20
+ afterScore: BrandAlignmentScoreResult;
21
+ usedAi: boolean;
22
+ }
23
+ export declare function needsAiBrandFix(score: BrandAlignmentScoreResult): boolean;
24
+ /** Score before/after deterministic fixes; caller runs AI when `needsAiBrandFix`. */
25
+ export declare function runDeterministicBrandFix(content: string, structured: BrandVoiceStructuredProfile, channel?: string): BrandVoiceFixOutcome;
26
+ //# sourceMappingURL=brand-voice-fix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-fix.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-fix.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAA;AAIpC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,sEAAsE;AACtE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,2BAA2B,EACvC,SAAS,CAAC,EAAE,MAAM,GACjB,sBAAsB,CA2CxB;AAMD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,mBAAmB,EAAE,EAC7B,UAAU,EAAE,2BAA2B,GACtC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA8BpC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,WAAW,EAAE,yBAAyB,CAAA;IACtC,UAAU,EAAE,yBAAyB,CAAA;IACrC,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAGzE;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,2BAA2B,EACvC,OAAO,CAAC,EAAE,MAAM,GACf,oBAAoB,CAWtB"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Hybrid brand-voice fixer — deterministic rule fixes first, optional AI rewrite
3
+ * for remaining alignment issues.
4
+ */
5
+ import { scoreBrandAlignment, } from './brand-voice-structured.js';
6
+ const EMOJI_RE = /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}]/gu;
7
+ /** Apply safe, deterministic fixes from structured rules (no LLM). */
8
+ export function applyDeterministicBrandFixes(content, structured, channelId) {
9
+ let text = content;
10
+ const fixes = [];
11
+ for (const banned of structured.bannedTerms) {
12
+ const term = banned.term.trim();
13
+ if (!term)
14
+ continue;
15
+ const re = new RegExp(`\\b${escapeRegExp(term)}\\b`, 'gi');
16
+ if (!re.test(text))
17
+ continue;
18
+ const replacement = banned.useInstead.trim() || '';
19
+ text = text.replace(re, (match) => {
20
+ if (!replacement)
21
+ return '';
22
+ if (match === match.toUpperCase())
23
+ return replacement.toUpperCase();
24
+ if (match[0] === match[0]?.toUpperCase()) {
25
+ return replacement.charAt(0).toUpperCase() + replacement.slice(1);
26
+ }
27
+ return replacement;
28
+ });
29
+ fixes.push(replacement
30
+ ? `Replaced banned term "${term}" with "${replacement}".`
31
+ : `Removed banned term "${term}".`);
32
+ }
33
+ if (structured.mechanics.exclamationPoints === 'forbid' && text.includes('!')) {
34
+ text = text.replace(/!+/g, '.');
35
+ fixes.push('Removed exclamation points.');
36
+ }
37
+ if (structured.mechanics.emoji === 'forbid' && EMOJI_RE.test(text)) {
38
+ text = text.replace(EMOJI_RE, '').replace(/\s{2,}/g, ' ');
39
+ fixes.push('Removed emoji.');
40
+ }
41
+ const channelRule = channelId ? structured.channels.find((c) => c.id === channelId) : undefined;
42
+ const limit = channelRule?.maxChars ?? channelRule?.maxDescriptionChars;
43
+ if (typeof limit === 'number' && text.length > limit) {
44
+ text = text.slice(0, limit).trimEnd();
45
+ fixes.push(`Trimmed to ${limit} characters for ${channelRule.label}.`);
46
+ }
47
+ return { text: text.trim(), fixes };
48
+ }
49
+ function escapeRegExp(value) {
50
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
51
+ }
52
+ export function buildBrandVoiceFixPrompt(content, issues, structured) {
53
+ const issueLines = issues
54
+ .slice(0, 12)
55
+ .map((i) => `- ${i.message}${i.suggestion ? ` → ${i.suggestion}` : ''}`)
56
+ .join('\n');
57
+ const preferred = structured.preferredTerms.length > 0
58
+ ? `Preferred terms: ${structured.preferredTerms.slice(0, 12).join(', ')}`
59
+ : '';
60
+ return {
61
+ system: 'You are a brand voice editor. Rewrite the supplied copy so it matches the brand ' +
62
+ 'voice rules. Preserve the original meaning and approximate length unless a channel ' +
63
+ 'character limit requires shortening. Return only the revised copy — no quotes, ' +
64
+ 'no preamble, no markdown.',
65
+ prompt: [
66
+ 'Fix this copy to resolve the alignment issues below.',
67
+ preferred,
68
+ '',
69
+ 'Issues:',
70
+ issueLines || '- General off-brand tone or vocabulary.',
71
+ '',
72
+ 'Copy to fix:',
73
+ `<<<COPY\n${content.slice(0, 4000)}\n>>>`,
74
+ ]
75
+ .filter(Boolean)
76
+ .join('\n'),
77
+ };
78
+ }
79
+ export function needsAiBrandFix(score) {
80
+ if (score.score >= 90)
81
+ return false;
82
+ return score.issues.some((i) => i.severity === 'error' || i.severity === 'warning');
83
+ }
84
+ /** Score before/after deterministic fixes; caller runs AI when `needsAiBrandFix`. */
85
+ export function runDeterministicBrandFix(content, structured, channel) {
86
+ const beforeScore = scoreBrandAlignment(content, structured, channel);
87
+ const { text, fixes } = applyDeterministicBrandFixes(content, structured, channel);
88
+ const afterScore = scoreBrandAlignment(text, structured, channel);
89
+ return {
90
+ text,
91
+ deterministicFixes: fixes,
92
+ beforeScore,
93
+ afterScore,
94
+ usedAi: false,
95
+ };
96
+ }
97
+ //# sourceMappingURL=brand-voice-fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-fix.js","sourceRoot":"","sources":["../../src/ai/brand-voice-fix.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,mBAAmB,GAIpB,MAAM,6BAA6B,CAAA;AAEpC,MAAM,QAAQ,GAAG,0CAA0C,CAAA;AAO3D,sEAAsE;AACtE,MAAM,UAAU,4BAA4B,CAC1C,OAAe,EACf,UAAuC,EACvC,SAAkB;IAElB,IAAI,IAAI,GAAG,OAAO,CAAA;IAClB,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,MAAM,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;QAC/B,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1D,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC5B,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;QAClD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,IAAI,CAAC,WAAW;gBAAE,OAAO,EAAE,CAAA;YAC3B,IAAI,KAAK,KAAK,KAAK,CAAC,WAAW,EAAE;gBAAE,OAAO,WAAW,CAAC,WAAW,EAAE,CAAA;YACnE,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,CAAC;gBACzC,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACnE,CAAC;YACD,OAAO,WAAW,CAAA;QACpB,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,IAAI,CACR,WAAW;YACT,CAAC,CAAC,yBAAyB,IAAI,WAAW,WAAW,IAAI;YACzD,CAAC,CAAC,wBAAwB,IAAI,IAAI,CACrC,CAAA;IACH,CAAC;IAED,IAAI,UAAU,CAAC,SAAS,CAAC,iBAAiB,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9E,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC/B,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAA;IAC3C,CAAC;IAED,IAAI,UAAU,CAAC,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;QACzD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC/F,MAAM,KAAK,GAAG,WAAW,EAAE,QAAQ,IAAI,WAAW,EAAE,mBAAmB,CAAA;IACvE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACrD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;QACrC,KAAK,CAAC,IAAI,CAAC,cAAc,KAAK,mBAAmB,WAAY,CAAC,KAAK,GAAG,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAA;AACrC,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,MAA6B,EAC7B,UAAuC;IAEvC,MAAM,UAAU,GAAG,MAAM;SACtB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SACvE,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,SAAS,GACb,UAAU,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;QAClC,CAAC,CAAC,oBAAoB,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACzE,CAAC,CAAC,EAAE,CAAA;IAER,OAAO;QACL,MAAM,EACJ,kFAAkF;YAClF,qFAAqF;YACrF,iFAAiF;YACjF,2BAA2B;QAC7B,MAAM,EAAE;YACN,sDAAsD;YACtD,SAAS;YACT,EAAE;YACF,SAAS;YACT,UAAU,IAAI,yCAAyC;YACvD,EAAE;YACF,cAAc;YACd,YAAY,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO;SAC1C;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC;KACd,CAAA;AACH,CAAC;AAUD,MAAM,UAAU,eAAe,CAAC,KAAgC;IAC9D,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE;QAAE,OAAO,KAAK,CAAA;IACnC,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAA;AACrF,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,UAAuC,EACvC,OAAgB;IAEhB,MAAM,WAAW,GAAG,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,4BAA4B,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAClF,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACjE,OAAO;QACL,IAAI;QACJ,kBAAkB,EAAE,KAAK;QACzB,WAAW;QACX,UAAU;QACV,MAAM,EAAE,KAAK;KACd,CAAA;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BrandVoiceProfile } from './types.js';
2
+ export interface PrepareBrandVoiceOptions {
3
+ /** When true, trust the markdown document over quick-edit fields. */
4
+ preferDocument?: boolean;
5
+ }
6
+ /**
7
+ * Normalize a brand voice payload for persistence. Quick-edit fields and the
8
+ * markdown document stay in sync — whichever surface the editor used last wins.
9
+ */
10
+ export declare function prepareBrandVoiceForSave(input: Partial<BrandVoiceProfile> & PrepareBrandVoiceOptions): BrandVoiceProfile;
11
+ //# sourceMappingURL=brand-voice-save.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-save.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-save.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAiBD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,wBAAwB,GAC3D,iBAAiB,CAiCnB"}
@@ -0,0 +1,51 @@
1
+ import { normalizeBrandVoice } from './brand-voice.js';
2
+ import { applyParsedDocumentToProfile, parseBrandVoiceDocument, serializeBrandVoiceDocument, } from './brand-voice-document.js';
3
+ import { parseStructuredProfile } from './brand-voice-structured.js';
4
+ function metaFromProfile(profile) {
5
+ return {
6
+ version: profile.version ?? 1,
7
+ status: profile.status ?? 'draft',
8
+ generatedFrom: profile.generatedFrom ?? [],
9
+ lastUpdated: new Date().toISOString().slice(0, 10),
10
+ };
11
+ }
12
+ function fromParsed(enabled, parsed) {
13
+ return normalizeBrandVoice(applyParsedDocumentToProfile({ enabled }, parsed));
14
+ }
15
+ /**
16
+ * Normalize a brand voice payload for persistence. Quick-edit fields and the
17
+ * markdown document stay in sync — whichever surface the editor used last wins.
18
+ */
19
+ export function prepareBrandVoiceForSave(input) {
20
+ const enabled = Boolean(input.enabled);
21
+ const fields = {
22
+ name: input.name ?? '',
23
+ tone: input.tone ?? '',
24
+ audience: input.audience ?? '',
25
+ guidelines: input.guidelines ?? '',
26
+ sampleText: input.sampleText ?? '',
27
+ };
28
+ if (input.preferDocument && input.document?.trim()) {
29
+ const parsed = parseBrandVoiceDocument(input.document);
30
+ if (!parsed.ok)
31
+ throw new Error(parsed.error);
32
+ return fromParsed(enabled, parsed.value);
33
+ }
34
+ const meta = metaFromProfile(input);
35
+ const structured = input.document?.trim() ? parseStructuredProfile(input.document) : undefined;
36
+ const document = serializeBrandVoiceDocument({
37
+ fields,
38
+ meta: { ...meta, version: (input.version ?? 1) + 1 },
39
+ existingDocument: input.document,
40
+ structured,
41
+ });
42
+ return normalizeBrandVoice({
43
+ enabled,
44
+ ...fields,
45
+ document,
46
+ version: (input.version ?? 1) + 1,
47
+ status: meta.status,
48
+ generatedFrom: meta.generatedFrom.length ? meta.generatedFrom : undefined,
49
+ });
50
+ }
51
+ //# sourceMappingURL=brand-voice-save.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-save.js","sourceRoot":"","sources":["../../src/ai/brand-voice-save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EACL,4BAA4B,EAC5B,uBAAuB,EACvB,2BAA2B,GAE5B,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AAQpE,SAAS,eAAe,CAAC,OAAmC;IAC1D,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO;QACjC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,EAAE;QAC1C,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;KAC1C,CAAA;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB,EAAE,MAAgC;IACpE,OAAO,mBAAmB,CACxB,4BAA4B,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAA+B,CAChF,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAA4D;IAE5D,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;QAClC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;KACnC,CAAA;IAED,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC7C,OAAO,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACnC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9F,MAAM,QAAQ,GAAG,2BAA2B,CAAC;QAC3C,MAAM;QACN,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE;QACpD,gBAAgB,EAAE,KAAK,CAAC,QAAQ;QAChC,UAAU;KACX,CAAC,CAAA;IAEF,OAAO,mBAAmB,CAAC;QACzB,OAAO;QACP,GAAG,MAAM;QACT,QAAQ;QACR,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC;QACjC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * External sources for brand voice analysis — SSRF-safe URL fetch and pasted uploads.
3
+ */
4
+ import type { BrandVoiceSample } from './brand-voice-analyze.js';
5
+ export declare const MAX_ANALYZE_URLS = 5;
6
+ export declare const MAX_ANALYZE_UPLOADS = 5;
7
+ export declare const MAX_UPLOAD_TEXT_CHARS = 15000;
8
+ export declare const MAX_URL_FETCH_CHARS = 8000;
9
+ export declare const MAX_URL_FETCH_BYTES: number;
10
+ export declare class BrandVoiceSourceError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ /** Fetch and extract readable text from a public URL (SSRF-safe, no redirects). */
14
+ export declare function fetchBrandVoiceUrlSample(url: string): Promise<BrandVoiceSample>;
15
+ export declare function uploadToBrandVoiceSample(name: string, text: string): BrandVoiceSample;
16
+ //# sourceMappingURL=brand-voice-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-sources.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-sources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAC3C,eAAO,MAAM,mBAAmB,OAAQ,CAAA;AACxC,eAAO,MAAM,mBAAmB,QAAa,CAAA;AAqB7C,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,mFAAmF;AACnF,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4DrF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAgBrF"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * External sources for brand voice analysis — SSRF-safe URL fetch and pasted uploads.
3
+ */
4
+ import { safeFetch, SsrfBlockedError } from '../security/safe-fetch.js';
5
+ import { stripHtmlTags } from '../seo/analysis.js';
6
+ export const MAX_ANALYZE_URLS = 5;
7
+ export const MAX_ANALYZE_UPLOADS = 5;
8
+ export const MAX_UPLOAD_TEXT_CHARS = 15_000;
9
+ export const MAX_URL_FETCH_CHARS = 8_000;
10
+ export const MAX_URL_FETCH_BYTES = 512 * 1024;
11
+ function clampText(text, max) {
12
+ return text.replace(/\s+/g, ' ').trim().slice(0, max);
13
+ }
14
+ function extractHtmlTitle(html) {
15
+ const match = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
16
+ if (!match?.[1])
17
+ return 'Web page';
18
+ return stripHtmlTags(match[1]).slice(0, 200) || 'Web page';
19
+ }
20
+ function extractReadableText(html) {
21
+ const withoutScripts = html
22
+ .replace(/<script[\s\S]*?<\/script>/gi, ' ')
23
+ .replace(/<style[\s\S]*?<\/style>/gi, ' ')
24
+ .replace(/<nav[\s\S]*?<\/nav>/gi, ' ')
25
+ .replace(/<footer[\s\S]*?<\/footer>/gi, ' ');
26
+ return clampText(stripHtmlTags(withoutScripts), MAX_URL_FETCH_CHARS);
27
+ }
28
+ export class BrandVoiceSourceError extends Error {
29
+ constructor(message) {
30
+ super(message);
31
+ this.name = 'BrandVoiceSourceError';
32
+ }
33
+ }
34
+ /** Fetch and extract readable text from a public URL (SSRF-safe, no redirects). */
35
+ export async function fetchBrandVoiceUrlSample(url) {
36
+ let parsed;
37
+ try {
38
+ parsed = new URL(url);
39
+ }
40
+ catch {
41
+ throw new BrandVoiceSourceError(`Invalid URL: ${url}`);
42
+ }
43
+ if (!['http:', 'https:'].includes(parsed.protocol)) {
44
+ throw new BrandVoiceSourceError('Only http and https URLs are supported.');
45
+ }
46
+ let response;
47
+ try {
48
+ response = await safeFetch(url, { method: 'GET', timeoutMs: 8000, followRedirects: false });
49
+ }
50
+ catch (err) {
51
+ if (err instanceof SsrfBlockedError) {
52
+ throw new BrandVoiceSourceError(`URL blocked for security: ${url}`);
53
+ }
54
+ throw new BrandVoiceSourceError(`Could not fetch ${url}`);
55
+ }
56
+ if (!response.ok) {
57
+ throw new BrandVoiceSourceError(`URL returned HTTP ${response.status}: ${url}`);
58
+ }
59
+ const contentType = (response.headers.get('content-type') ?? '').split(';')[0]?.trim() ?? '';
60
+ const buffer = Buffer.from(await response.arrayBuffer());
61
+ if (buffer.byteLength === 0) {
62
+ throw new BrandVoiceSourceError(`Empty response from ${url}`);
63
+ }
64
+ if (buffer.byteLength > MAX_URL_FETCH_BYTES) {
65
+ throw new BrandVoiceSourceError(`Response too large from ${url}`);
66
+ }
67
+ const raw = buffer.toString('utf-8');
68
+ let text;
69
+ let title;
70
+ if (contentType.includes('text/html') || raw.includes('<html')) {
71
+ title = extractHtmlTitle(raw);
72
+ text = extractReadableText(raw);
73
+ }
74
+ else if (contentType.startsWith('text/') || contentType.includes('markdown')) {
75
+ title = parsed.hostname;
76
+ text = clampText(raw, MAX_URL_FETCH_CHARS);
77
+ }
78
+ else {
79
+ throw new BrandVoiceSourceError(`Unsupported content type "${contentType || 'unknown'}" from ${url}`);
80
+ }
81
+ if (!text) {
82
+ throw new BrandVoiceSourceError(`No readable text found at ${url}`);
83
+ }
84
+ return {
85
+ collection: 'url',
86
+ documentId: url,
87
+ title,
88
+ text,
89
+ };
90
+ }
91
+ export function uploadToBrandVoiceSample(name, text) {
92
+ const trimmed = text.trim();
93
+ if (!trimmed) {
94
+ throw new BrandVoiceSourceError(`Upload "${name}" is empty.`);
95
+ }
96
+ if (trimmed.length > MAX_UPLOAD_TEXT_CHARS) {
97
+ throw new BrandVoiceSourceError(`Upload "${name}" exceeds ${MAX_UPLOAD_TEXT_CHARS.toLocaleString()} characters.`);
98
+ }
99
+ return {
100
+ collection: 'upload',
101
+ documentId: name.slice(0, 120),
102
+ title: name.slice(0, 200) || 'Uploaded document',
103
+ text: trimmed,
104
+ };
105
+ }
106
+ //# sourceMappingURL=brand-voice-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-sources.js","sourceRoot":"","sources":["../../src/ai/brand-voice-sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Structured brand voice rules — parsed from profile frontmatter and used for
3
+ * deterministic alignment scoring and the Phase 3 admin editors.
4
+ */
5
+ export type ContractionsRule = 'allow' | 'avoid' | 'require';
6
+ export type ExclamationRule = 'allow' | 'avoid' | 'forbid';
7
+ export type EmojiRule = 'allow' | 'sparingly' | 'forbid';
8
+ export type CaseRule = 'sentence' | 'title';
9
+ export type NumbersRule = 'spell_all' | 'numerals_all' | 'numerals_for_10_plus';
10
+ export type SpellingRule = 'us' | 'uk';
11
+ export interface BrandVoiceToneScales {
12
+ formalCasual: number;
13
+ seriousPlayful: number;
14
+ conciseDetailed: number;
15
+ reservedBold: number;
16
+ readingLevel: number;
17
+ }
18
+ export interface BrandVoiceMechanics {
19
+ oxfordComma: boolean;
20
+ contractions: ContractionsRule;
21
+ exclamationPoints: ExclamationRule;
22
+ emoji: EmojiRule;
23
+ caseHeadlines: CaseRule;
24
+ caseButtons: CaseRule;
25
+ numbers: NumbersRule;
26
+ serialSpelling: SpellingRule;
27
+ }
28
+ export interface BrandVoiceBannedTerm {
29
+ term: string;
30
+ useInstead: string;
31
+ }
32
+ export interface BrandVoiceChannelRule {
33
+ id: string;
34
+ label: string;
35
+ maxChars?: number;
36
+ maxHeadlineChars?: number;
37
+ maxDescriptionChars?: number;
38
+ note?: string;
39
+ }
40
+ export interface BrandVoiceWorkedExample {
41
+ offBrand: string;
42
+ onBrand: string;
43
+ why: string;
44
+ }
45
+ export interface BrandVoiceStructuredProfile {
46
+ tone: BrandVoiceToneScales;
47
+ mechanics: BrandVoiceMechanics;
48
+ bannedTerms: BrandVoiceBannedTerm[];
49
+ preferredTerms: string[];
50
+ avoidPatterns: string[];
51
+ channels: BrandVoiceChannelRule[];
52
+ workedExamples: BrandVoiceWorkedExample[];
53
+ }
54
+ export interface BrandAlignmentIssue {
55
+ id: string;
56
+ severity: 'error' | 'warning' | 'info';
57
+ message: string;
58
+ suggestion?: string;
59
+ }
60
+ export interface BrandAlignmentDimension {
61
+ name: string;
62
+ score: number;
63
+ feedback: string;
64
+ }
65
+ export interface BrandAlignmentScoreResult {
66
+ score: number;
67
+ channel?: string;
68
+ issues: BrandAlignmentIssue[];
69
+ dimensions: BrandAlignmentDimension[];
70
+ }
71
+ export declare const BRAND_VOICE_CHANNEL_PRESETS: Array<{
72
+ id: string;
73
+ label: string;
74
+ }>;
75
+ declare const MAX_WORKED_EXAMPLES = 10;
76
+ declare const MAX_BANNED_TERMS = 40;
77
+ declare const MAX_PREFERRED_TERMS = 40;
78
+ declare const MAX_AVOID_PATTERNS = 20;
79
+ export declare function defaultStructuredProfile(): BrandVoiceStructuredProfile;
80
+ /** Parse machine-readable rules from a profile markdown document. */
81
+ export declare function parseStructuredProfile(document: string): BrandVoiceStructuredProfile;
82
+ /** Apply structured rule edits back into the profile markdown document. */
83
+ export declare function applyStructuredProfileToDocument(document: string, structured: BrandVoiceStructuredProfile): string;
84
+ /** Deterministic alignment score using frontmatter rules. */
85
+ export declare function scoreBrandAlignment(content: string, structured: BrandVoiceStructuredProfile, channelId?: string): BrandAlignmentScoreResult;
86
+ export { MAX_BANNED_TERMS, MAX_PREFERRED_TERMS, MAX_AVOID_PATTERNS, MAX_WORKED_EXAMPLES };
87
+ //# sourceMappingURL=brand-voice-structured.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-voice-structured.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-structured.ts"],"names":[],"mappings":"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"}