@roughen/core 0.3.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.
- package/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/baselines.d.ts +55 -0
- package/dist/baselines.js +42 -0
- package/dist/baselines.js.map +1 -0
- package/dist/brief.d.ts +45 -0
- package/dist/brief.js +110 -0
- package/dist/brief.js.map +1 -0
- package/dist/document.d.ts +5 -0
- package/dist/document.js +531 -0
- package/dist/document.js.map +1 -0
- package/dist/editorial.d.ts +53 -0
- package/dist/editorial.js +252 -0
- package/dist/editorial.js.map +1 -0
- package/dist/fixes.d.ts +8 -0
- package/dist/fixes.js +53 -0
- package/dist/fixes.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown-links.d.ts +12 -0
- package/dist/markdown-links.js +130 -0
- package/dist/markdown-links.js.map +1 -0
- package/dist/preserve.d.ts +36 -0
- package/dist/preserve.js +83 -0
- package/dist/preserve.js.map +1 -0
- package/dist/protected-tokens.d.ts +7 -0
- package/dist/protected-tokens.js +103 -0
- package/dist/protected-tokens.js.map +1 -0
- package/dist/ranges.d.ts +13 -0
- package/dist/ranges.js +35 -0
- package/dist/ranges.js.map +1 -0
- package/dist/rules.d.ts +12 -0
- package/dist/rules.js +336 -0
- package/dist/rules.js.map +1 -0
- package/dist/sentences.d.ts +3 -0
- package/dist/sentences.js +95 -0
- package/dist/sentences.js.map +1 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist-cjs/baselines.d.ts +55 -0
- package/dist-cjs/baselines.js +45 -0
- package/dist-cjs/baselines.js.map +1 -0
- package/dist-cjs/brief.d.ts +45 -0
- package/dist-cjs/brief.js +114 -0
- package/dist-cjs/brief.js.map +1 -0
- package/dist-cjs/document.d.ts +5 -0
- package/dist-cjs/document.js +535 -0
- package/dist-cjs/document.js.map +1 -0
- package/dist-cjs/editorial.d.ts +53 -0
- package/dist-cjs/editorial.js +263 -0
- package/dist-cjs/editorial.js.map +1 -0
- package/dist-cjs/fixes.d.ts +8 -0
- package/dist-cjs/fixes.js +57 -0
- package/dist-cjs/fixes.js.map +1 -0
- package/dist-cjs/index.d.ts +73 -0
- package/dist-cjs/index.js +258 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/markdown-links.d.ts +12 -0
- package/dist-cjs/markdown-links.js +134 -0
- package/dist-cjs/markdown-links.js.map +1 -0
- package/dist-cjs/package.json +1 -0
- package/dist-cjs/preserve.d.ts +36 -0
- package/dist-cjs/preserve.js +87 -0
- package/dist-cjs/preserve.js.map +1 -0
- package/dist-cjs/protected-tokens.d.ts +7 -0
- package/dist-cjs/protected-tokens.js +106 -0
- package/dist-cjs/protected-tokens.js.map +1 -0
- package/dist-cjs/ranges.d.ts +13 -0
- package/dist-cjs/ranges.js +41 -0
- package/dist-cjs/ranges.js.map +1 -0
- package/dist-cjs/rules.d.ts +12 -0
- package/dist-cjs/rules.js +340 -0
- package/dist-cjs/rules.js.map +1 -0
- package/dist-cjs/sentences.d.ts +3 -0
- package/dist-cjs/sentences.js +98 -0
- package/dist-cjs/sentences.js.map +1 -0
- package/dist-cjs/types.d.ts +140 -0
- package/dist-cjs/types.js +3 -0
- package/dist-cjs/types.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.version = exports.baselines = exports.revisionLengthTolerance = exports.comparePreservation = exports.scopeFindings = exports.revisionBrief = exports.collectMarkdownLinkTargets = exports.applyFixes = exports.segmentSentences = exports.parseDocument = exports.longSentenceVerdict = exports.densityRuleIds = exports.densityVerdict = exports.densityMinimumInstances = exports.densityMinimumWords = exports.densityBaseline = exports.bodyBlocks = void 0;
|
|
4
|
+
exports.listRules = listRules;
|
|
5
|
+
exports.profileText = profileText;
|
|
6
|
+
exports.countBanned = countBanned;
|
|
7
|
+
exports.countFlagged = countFlagged;
|
|
8
|
+
exports.lint = lint;
|
|
9
|
+
exports.isRevisable = isRevisable;
|
|
10
|
+
exports.revisableRules = revisableRules;
|
|
11
|
+
exports.judgeRevision = judgeRevision;
|
|
12
|
+
const document_js_1 = require("./document.js");
|
|
13
|
+
Object.defineProperty(exports, "parseDocument", { enumerable: true, get: function () { return document_js_1.parseDocument; } });
|
|
14
|
+
const sentences_js_1 = require("./sentences.js");
|
|
15
|
+
Object.defineProperty(exports, "segmentSentences", { enumerable: true, get: function () { return sentences_js_1.segmentSentences; } });
|
|
16
|
+
const fixes_js_1 = require("./fixes.js");
|
|
17
|
+
Object.defineProperty(exports, "applyFixes", { enumerable: true, get: function () { return fixes_js_1.applyFixes; } });
|
|
18
|
+
const rules_js_1 = require("./rules.js");
|
|
19
|
+
const ranges_js_1 = require("./ranges.js");
|
|
20
|
+
const editorial_js_1 = require("./editorial.js");
|
|
21
|
+
var editorial_js_2 = require("./editorial.js");
|
|
22
|
+
Object.defineProperty(exports, "bodyBlocks", { enumerable: true, get: function () { return editorial_js_2.bodyBlocks; } });
|
|
23
|
+
Object.defineProperty(exports, "densityBaseline", { enumerable: true, get: function () { return editorial_js_2.densityBaseline; } });
|
|
24
|
+
Object.defineProperty(exports, "densityMinimumWords", { enumerable: true, get: function () { return editorial_js_2.densityMinimumWords; } });
|
|
25
|
+
Object.defineProperty(exports, "densityMinimumInstances", { enumerable: true, get: function () { return editorial_js_2.densityMinimumInstances; } });
|
|
26
|
+
Object.defineProperty(exports, "densityVerdict", { enumerable: true, get: function () { return editorial_js_2.densityVerdict; } });
|
|
27
|
+
Object.defineProperty(exports, "densityRuleIds", { enumerable: true, get: function () { return editorial_js_2.densityRuleIds; } });
|
|
28
|
+
Object.defineProperty(exports, "longSentenceVerdict", { enumerable: true, get: function () { return editorial_js_2.longSentenceVerdict; } });
|
|
29
|
+
const editorial_js_3 = require("./editorial.js");
|
|
30
|
+
var document_js_2 = require("./document.js");
|
|
31
|
+
Object.defineProperty(exports, "collectMarkdownLinkTargets", { enumerable: true, get: function () { return document_js_2.collectMarkdownLinkTargets; } });
|
|
32
|
+
var brief_js_1 = require("./brief.js");
|
|
33
|
+
Object.defineProperty(exports, "revisionBrief", { enumerable: true, get: function () { return brief_js_1.revisionBrief; } });
|
|
34
|
+
Object.defineProperty(exports, "scopeFindings", { enumerable: true, get: function () { return brief_js_1.scopeFindings; } });
|
|
35
|
+
var preserve_js_1 = require("./preserve.js");
|
|
36
|
+
Object.defineProperty(exports, "comparePreservation", { enumerable: true, get: function () { return preserve_js_1.comparePreservation; } });
|
|
37
|
+
Object.defineProperty(exports, "revisionLengthTolerance", { enumerable: true, get: function () { return preserve_js_1.revisionLengthTolerance; } });
|
|
38
|
+
const preserve_js_2 = require("./preserve.js");
|
|
39
|
+
var baselines_js_1 = require("./baselines.js");
|
|
40
|
+
Object.defineProperty(exports, "baselines", { enumerable: true, get: function () { return baselines_js_1.baselines; } });
|
|
41
|
+
exports.version = '0.3.0';
|
|
42
|
+
/** Rule descriptions only. Consumers cannot mutate the engine's registry. */
|
|
43
|
+
function listRules() {
|
|
44
|
+
return rules_js_1.rules.map(({ id, severity, description }) => ({ id, severity, description }));
|
|
45
|
+
}
|
|
46
|
+
function locationReader(source) {
|
|
47
|
+
const starts = [0];
|
|
48
|
+
for (let i = 0; i < source.length; i++)
|
|
49
|
+
if (source[i] === '\n')
|
|
50
|
+
starts.push(i + 1);
|
|
51
|
+
return (offset) => {
|
|
52
|
+
let lo = 0;
|
|
53
|
+
let hi = starts.length;
|
|
54
|
+
while (lo + 1 < hi) {
|
|
55
|
+
const mid = (lo + hi) >>> 1;
|
|
56
|
+
if (starts[mid] <= offset)
|
|
57
|
+
lo = mid;
|
|
58
|
+
else
|
|
59
|
+
hi = mid;
|
|
60
|
+
}
|
|
61
|
+
return { line: lo + 1, column: offset - starts[lo] + 1 };
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const registers = ['article', 'marketing'];
|
|
65
|
+
const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
66
|
+
function validateConfig(config) {
|
|
67
|
+
if (!isObject(config))
|
|
68
|
+
throw new TypeError('Roughen config must be an object');
|
|
69
|
+
const keys = new Set(['format', 'fix', 'careful', 'register', 'rules', 'voice']);
|
|
70
|
+
for (const key of Object.keys(config))
|
|
71
|
+
if (!keys.has(key))
|
|
72
|
+
throw new Error(`Unsupported Roughen config option: ${key}`);
|
|
73
|
+
if (config.register !== undefined && !registers.includes(config.register))
|
|
74
|
+
throw new Error(`Unsupported Roughen register: ${String(config.register)}; use ${registers.join(' or ')}`);
|
|
75
|
+
const voice = config.voice;
|
|
76
|
+
if (voice !== undefined && !isObject(voice))
|
|
77
|
+
throw new TypeError('Roughen voice must be an object');
|
|
78
|
+
for (const key of Object.keys(voice ?? {})) {
|
|
79
|
+
if (!['banned', 'required', 'bannedCharacters', 'bannedPatterns'].includes(key))
|
|
80
|
+
throw new Error(`Unsupported Roughen voice option: ${key}`);
|
|
81
|
+
}
|
|
82
|
+
const { banned, required, bannedCharacters, bannedPatterns } = (voice ?? {});
|
|
83
|
+
const optionalString = (value) => value === undefined || typeof value === 'string';
|
|
84
|
+
if (bannedCharacters !== undefined && !(Array.isArray(bannedCharacters) && bannedCharacters.every((entry) => (typeof entry === 'string' && entry.length > 0)
|
|
85
|
+
|| (isObject(entry) && typeof entry.character === 'string' && entry.character.length > 0 && optionalString(entry.use) && optionalString(entry.why))))) {
|
|
86
|
+
throw new TypeError('Roughen voice.bannedCharacters must be an array of non-empty strings or { character: string, use?: string, why?: string } entries');
|
|
87
|
+
}
|
|
88
|
+
if (bannedPatterns !== undefined && !(Array.isArray(bannedPatterns) && bannedPatterns.every((entry) => isObject(entry) && typeof entry.pattern === 'string' && entry.pattern.length > 0
|
|
89
|
+
&& optionalString(entry.flags) && optionalString(entry.message) && optionalString(entry.use)))) {
|
|
90
|
+
throw new TypeError('Roughen voice.bannedPatterns must be an array of { pattern: string, flags?: string, message?: string, use?: string } entries');
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
(0, rules_js_1.bannedPatternExpressions)(config);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
throw new TypeError(`Roughen voice.bannedPatterns has an invalid pattern: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
if (banned !== undefined && !(Array.isArray(banned) && banned.every((entry) => isObject(entry)
|
|
99
|
+
&& typeof entry.term === 'string' && (entry.use === undefined || typeof entry.use === 'string')))) {
|
|
100
|
+
throw new TypeError('Roughen voice.banned must be an array of { term: string, use?: string } entries');
|
|
101
|
+
}
|
|
102
|
+
if (required !== undefined && !(Array.isArray(required) && required.every((term) => typeof term === 'string'))) {
|
|
103
|
+
throw new TypeError('Roughen voice.required must be an array of strings');
|
|
104
|
+
}
|
|
105
|
+
const known = new Set(rules_js_1.rules.map((rule) => rule.id));
|
|
106
|
+
for (const [id, setting] of Object.entries(config.rules ?? {})) {
|
|
107
|
+
if (!known.has(id))
|
|
108
|
+
throw new Error(`Unknown or unavailable Roughen rule: ${id}`);
|
|
109
|
+
const severity = Array.isArray(setting) ? setting[0] : setting;
|
|
110
|
+
if (!['error', 'warn', 'info', 'off'].includes(severity))
|
|
111
|
+
throw new Error(`Invalid severity for ${id}`);
|
|
112
|
+
if (Array.isArray(setting) && (!setting[1] || typeof setting[1] !== 'object' || Array.isArray(setting[1]))) {
|
|
113
|
+
throw new Error(`Invalid options for ${id}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Body sentences, shared by the rhythm rule, the density profile and the metrics. */
|
|
118
|
+
function bodySentences(document) {
|
|
119
|
+
return (0, editorial_js_1.bodyBlocks)(document).flatMap((block) => (0, sentences_js_1.segmentSentences)(block.text, block.sourceRange[0]));
|
|
120
|
+
}
|
|
121
|
+
function analyze(source, config) {
|
|
122
|
+
const document = (0, document_js_1.parseDocument)(source, config.format ?? 'text');
|
|
123
|
+
const sentences = bodySentences(document);
|
|
124
|
+
const profile = (0, editorial_js_1.densityProfile)(document, sentences);
|
|
125
|
+
const locate = locationReader(source);
|
|
126
|
+
const findings = [];
|
|
127
|
+
for (const rule of rules_js_1.rules) {
|
|
128
|
+
const setting = config.rules?.[rule.id] ?? rule.severity;
|
|
129
|
+
const severity = Array.isArray(setting) ? setting[0] : setting;
|
|
130
|
+
if (severity === 'off')
|
|
131
|
+
continue;
|
|
132
|
+
const options = Array.isArray(setting) ? setting[1] : {};
|
|
133
|
+
for (const match of rule.check({ document, config, register: config.register ?? 'article', options, sentences, profile })) {
|
|
134
|
+
const [start, end] = match.range;
|
|
135
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start || end > source.length) {
|
|
136
|
+
throw new RangeError(`Invalid finding range from ${rule.id}`);
|
|
137
|
+
}
|
|
138
|
+
// The engine enforces protected source ranges even if a rule forgets.
|
|
139
|
+
if ((0, ranges_js_1.sortedRangesIntersect)(document.protectedRanges, match.range))
|
|
140
|
+
continue;
|
|
141
|
+
if (match.fix && (0, ranges_js_1.sortedRangesIntersect)(document.protectedRanges, match.fix.range))
|
|
142
|
+
continue;
|
|
143
|
+
const first = locate(start);
|
|
144
|
+
const last = locate(end);
|
|
145
|
+
findings.push({ ...match, ruleId: rule.id, severity, loc: { ...first, endLine: last.line, endColumn: last.column } });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Stable sorting preserves registry priority for findings at the same offset.
|
|
149
|
+
findings.sort((a, b) => a.range[0] - b.range[0]);
|
|
150
|
+
return { document, sentences, findings };
|
|
151
|
+
}
|
|
152
|
+
/** Profile of a text as Roughen measures it: body words and density-rule instances. For baselines and reports. */
|
|
153
|
+
function profileText(source, format = 'text') {
|
|
154
|
+
const document = (0, document_js_1.parseDocument)(source, format);
|
|
155
|
+
const sentences = bodySentences(document);
|
|
156
|
+
return { ...(0, editorial_js_1.densityProfile)(document, sentences), sentences, metrics: metricsFor(sentences) };
|
|
157
|
+
}
|
|
158
|
+
const banRules = ['voice/banned-character', 'voice/banned-pattern'];
|
|
159
|
+
/** Occurrences of what the project bans outright (voice.bannedCharacters, voice.bannedPatterns). */
|
|
160
|
+
function countBanned(source, voice, format = 'md') {
|
|
161
|
+
if (!voice?.bannedCharacters?.length && !voice?.bannedPatterns?.length)
|
|
162
|
+
return 0;
|
|
163
|
+
const only = Object.fromEntries(rules_js_1.rules.map((rule) => [rule.id, banRules.includes(rule.id) ? 'error' : 'off']));
|
|
164
|
+
return lint(source, { format, rules: only, voice: { bannedCharacters: voice.bannedCharacters, bannedPatterns: voice.bannedPatterns } }).findings.length;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* How many instances of the given rules' habits `source` contains, whether or
|
|
168
|
+
* not the rules fire. Banned characters and patterns count when `voice` names them.
|
|
169
|
+
*/
|
|
170
|
+
function countFlagged(source, ruleIds, format = 'md', voice) {
|
|
171
|
+
const { sentences, ...profile } = profileText(source, format);
|
|
172
|
+
const banned = ruleIds.some((id) => banRules.includes(id)) ? countBanned(source, voice, format) : 0;
|
|
173
|
+
return (0, editorial_js_3.flaggedInstances)(profile, sentences, ruleIds) + banned;
|
|
174
|
+
}
|
|
175
|
+
function metricsFor(sentences) {
|
|
176
|
+
const words = sentences.map((sentence) => sentence.wordCount).filter((count) => count > 0);
|
|
177
|
+
const total = words.reduce((sum, value) => sum + value, 0);
|
|
178
|
+
const mean = words.length ? total / words.length : null;
|
|
179
|
+
const enough = words.length >= 8 && mean !== null && mean > 0;
|
|
180
|
+
return {
|
|
181
|
+
sentenceWords: words,
|
|
182
|
+
sentenceCount: words.length,
|
|
183
|
+
wordCount: total,
|
|
184
|
+
meanSentenceLength: mean,
|
|
185
|
+
sentenceLengthCV: enough ? Math.sqrt(words.reduce((sum, value) => sum + (value - mean) ** 2, 0) / words.length) / mean : null,
|
|
186
|
+
meanAbsoluteSuccessiveDifference: enough ? words.slice(1).reduce((sum, value, i) => sum + Math.abs(value - words[i]), 0) / (words.length - 1) / mean : null,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/** Runs locally and deterministically. No network, model, locale, clock, or randomness. */
|
|
190
|
+
function lint(source, config = {}) {
|
|
191
|
+
if (typeof source !== 'string')
|
|
192
|
+
throw new TypeError('Roughen expects a string');
|
|
193
|
+
config ??= {};
|
|
194
|
+
validateConfig(config);
|
|
195
|
+
let output = source;
|
|
196
|
+
let state = analyze(output, config);
|
|
197
|
+
let applied = 0;
|
|
198
|
+
let passes = 0;
|
|
199
|
+
if (config.fix) {
|
|
200
|
+
while (passes < 3) {
|
|
201
|
+
const selected = (0, fixes_js_1.selectFixes)(output, state.findings, config);
|
|
202
|
+
if (!selected.length)
|
|
203
|
+
break;
|
|
204
|
+
output = (0, fixes_js_1.applyFixes)(output, selected, config);
|
|
205
|
+
applied += selected.length;
|
|
206
|
+
passes++;
|
|
207
|
+
state = analyze(output, config);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const converged = !config.fix || (0, fixes_js_1.selectFixes)(output, state.findings, config).length === 0;
|
|
211
|
+
const result = {
|
|
212
|
+
findings: state.findings,
|
|
213
|
+
score: null,
|
|
214
|
+
calibration: { status: 'unavailable', reason: 'Production thresholds have not been validated. Descriptive metrics are not an authorship or writing-quality score.' },
|
|
215
|
+
metrics: metricsFor(state.sentences),
|
|
216
|
+
};
|
|
217
|
+
if (config.fix) {
|
|
218
|
+
result.fixed = output;
|
|
219
|
+
result.fixes = { applied, passes, converged };
|
|
220
|
+
if (!converged) {
|
|
221
|
+
result.findings.push({ ruleId: 'engine/nonconvergent-fixes', severity: 'error', message: 'Automatic fixes did not converge after three passes.', why: 'Review the remaining edits manually; Roughen stopped to prevent a repeated rewrite loop.', range: [0, 0], loc: { line: 1, column: 1, endLine: 1, endColumn: 1 }, fixSafety: 'manual', confidence: 1 });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
/** A finding that asks for a rewrite: manual, warn or error, with a rewrite target. Info findings stay in the review. */
|
|
227
|
+
function isRevisable(finding) {
|
|
228
|
+
return finding.fixSafety === 'manual' && finding.severity !== 'info' && finding.evidence?.rewriteAtLeast !== undefined;
|
|
229
|
+
}
|
|
230
|
+
/** Rules whose findings ask for a rewrite. */
|
|
231
|
+
function revisableRules(result) {
|
|
232
|
+
return [...new Set(result.findings.filter(isRevisable).map((finding) => finding.ruleId))];
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Single source of truth for whether a rewrite should replace the original,
|
|
236
|
+
* used by Signal's revision gate and the MCP server. The revision must keep
|
|
237
|
+
* every figure, link, code sample, quotation and heading, add none, stay in
|
|
238
|
+
* length bounds, and reduce the flagged habits. `rules` defaults to the rules
|
|
239
|
+
* that asked for a rewrite of the original.
|
|
240
|
+
*/
|
|
241
|
+
function judgeRevision(original, revised, options = {}) {
|
|
242
|
+
const format = options.format ?? 'md';
|
|
243
|
+
const voice = options.voice ? { bannedCharacters: options.voice.bannedCharacters, bannedPatterns: options.voice.bannedPatterns } : undefined;
|
|
244
|
+
const rules = [...(options.rules ?? revisableRules(lint(original, { format, register: options.register, voice })))];
|
|
245
|
+
const preservation = (0, preserve_js_2.comparePreservation)(original, revised, { format, lengthTolerance: options.lengthTolerance });
|
|
246
|
+
const flaggedBefore = countFlagged(original, rules, format, voice);
|
|
247
|
+
const flaggedAfter = countFlagged(revised, rules, format, voice);
|
|
248
|
+
const reasons = [...preservation.reasons];
|
|
249
|
+
if (rules.length && flaggedAfter >= flaggedBefore)
|
|
250
|
+
reasons.push(`Flagged passages didn't decrease (${flaggedBefore} before, ${flaggedAfter} after).`);
|
|
251
|
+
// A ban is absolute: a revision may never add what the project forbids, flagged or not.
|
|
252
|
+
const bannedBefore = countBanned(original, voice, format);
|
|
253
|
+
const bannedAfter = countBanned(revised, voice, format);
|
|
254
|
+
if (bannedAfter > bannedBefore)
|
|
255
|
+
reasons.push(`The revision adds ${bannedAfter - bannedBefore} banned character(s) or pattern(s) (${bannedBefore} before, ${bannedAfter} after).`);
|
|
256
|
+
return { accepted: reasons.length === 0, rules, flaggedBefore, flaggedAfter, bannedBefore, bannedAfter, reasons, preservation };
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Range } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* GFM 6.6: ASCII whitespace separates a destination from an optional title.
|
|
4
|
+
* Quotes inside a URI stay literal. Malformed inline syntax remains opaque to
|
|
5
|
+
* the paragraph boundary, preserving the document parser's existing behavior.
|
|
6
|
+
*/
|
|
7
|
+
export declare function markdownLinkSyntax(source: string, opening: number): {
|
|
8
|
+
end: number;
|
|
9
|
+
destinationRange?: Range;
|
|
10
|
+
};
|
|
11
|
+
/** A definition contributes its literal target once; reference usage is not rendered. */
|
|
12
|
+
export declare function markdownReferenceDestination(source: string, afterColon: number): Range | null;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.markdownLinkSyntax = markdownLinkSyntax;
|
|
4
|
+
exports.markdownReferenceDestination = markdownReferenceDestination;
|
|
5
|
+
const whitespace = (char) => char !== undefined && /[ \t\r\n\f\v]/.test(char);
|
|
6
|
+
/** Shared destination grammar for document masking and literal target evidence. */
|
|
7
|
+
function destination(source, start, limit) {
|
|
8
|
+
let cursor = start;
|
|
9
|
+
if (source[cursor] === '<') {
|
|
10
|
+
for (cursor++; cursor < limit && source[cursor] !== '>'; cursor++) {
|
|
11
|
+
if (source[cursor] === '\\') {
|
|
12
|
+
cursor++;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (source[cursor] === '<' || /[\r\n]/.test(source[cursor]))
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (cursor >= limit || source[cursor] !== '>')
|
|
19
|
+
return null;
|
|
20
|
+
return { end: cursor + 1, range: [start + 1, cursor] };
|
|
21
|
+
}
|
|
22
|
+
let depth = 0;
|
|
23
|
+
for (; cursor < limit; cursor++) {
|
|
24
|
+
const char = source[cursor];
|
|
25
|
+
if (char === '\\') {
|
|
26
|
+
cursor++;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (char === '(')
|
|
30
|
+
depth++;
|
|
31
|
+
else if (char === ')') {
|
|
32
|
+
if (!depth)
|
|
33
|
+
break;
|
|
34
|
+
depth--;
|
|
35
|
+
}
|
|
36
|
+
else if (whitespace(char)) {
|
|
37
|
+
if (depth)
|
|
38
|
+
return null;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
else if (char === '<')
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return depth ? null : { end: cursor, range: [start, cursor] };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* GFM 6.6: ASCII whitespace separates a destination from an optional title.
|
|
48
|
+
* Quotes inside a URI stay literal. Malformed inline syntax remains opaque to
|
|
49
|
+
* the paragraph boundary, preserving the document parser's existing behavior.
|
|
50
|
+
*/
|
|
51
|
+
function markdownLinkSyntax(source, opening) {
|
|
52
|
+
const blankLine = /\r?\n[ \t]*\r?\n/.exec(source.slice(opening));
|
|
53
|
+
const limit = blankLine ? opening + blankLine.index : source.length;
|
|
54
|
+
const invalid = { end: limit };
|
|
55
|
+
let cursor = opening + 1;
|
|
56
|
+
while (cursor < limit && whitespace(source[cursor]))
|
|
57
|
+
cursor++;
|
|
58
|
+
if (source[cursor] === ')')
|
|
59
|
+
return { end: cursor + 1 };
|
|
60
|
+
if (cursor > opening + 1 && (source[cursor] === '"' || source[cursor] === "'")) {
|
|
61
|
+
const titleEnd = markdownLinkTitleEnd(source, cursor, limit);
|
|
62
|
+
if (titleEnd !== null)
|
|
63
|
+
return { end: titleEnd };
|
|
64
|
+
}
|
|
65
|
+
const target = destination(source, cursor, limit);
|
|
66
|
+
if (!target)
|
|
67
|
+
return invalid;
|
|
68
|
+
cursor = target.end;
|
|
69
|
+
while (cursor < limit && whitespace(source[cursor]))
|
|
70
|
+
cursor++;
|
|
71
|
+
if (cursor < limit && source[cursor] === ')')
|
|
72
|
+
return { end: cursor + 1, destinationRange: target.range };
|
|
73
|
+
if (cursor === target.end)
|
|
74
|
+
return invalid;
|
|
75
|
+
const end = markdownLinkTitleEnd(source, cursor, limit);
|
|
76
|
+
return end === null ? invalid : { end, destinationRange: target.range };
|
|
77
|
+
}
|
|
78
|
+
function markdownLinkTitleEnd(source, start, limit, inline = true) {
|
|
79
|
+
let cursor = start;
|
|
80
|
+
const titleOpen = source[cursor];
|
|
81
|
+
if (titleOpen !== '"' && titleOpen !== "'" && titleOpen !== '(')
|
|
82
|
+
return null;
|
|
83
|
+
const titleClose = titleOpen === '(' ? ')' : titleOpen;
|
|
84
|
+
for (cursor++; cursor < limit && source[cursor] !== titleClose; cursor++) {
|
|
85
|
+
if (source[cursor] === '\\') {
|
|
86
|
+
cursor++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (titleOpen === '(' && source[cursor] === '(')
|
|
90
|
+
return null;
|
|
91
|
+
if (source[cursor] === '\n' && /^[ \t]*\r?\n/.test(source.slice(cursor + 1)))
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
if (cursor >= limit || source[cursor] !== titleClose)
|
|
95
|
+
return null;
|
|
96
|
+
cursor++;
|
|
97
|
+
while (cursor < limit && whitespace(source[cursor]))
|
|
98
|
+
cursor++;
|
|
99
|
+
return inline ? cursor < limit && source[cursor] === ')' ? cursor + 1 : null : cursor === limit ? cursor : null;
|
|
100
|
+
}
|
|
101
|
+
/** A definition contributes its literal target once; reference usage is not rendered. */
|
|
102
|
+
function markdownReferenceDestination(source, afterColon) {
|
|
103
|
+
let cursor = afterColon;
|
|
104
|
+
while (source[cursor] === ' ' || source[cursor] === '\t')
|
|
105
|
+
cursor++;
|
|
106
|
+
if (source[cursor] === '\r')
|
|
107
|
+
cursor++;
|
|
108
|
+
if (source[cursor] === '\n') {
|
|
109
|
+
cursor++;
|
|
110
|
+
let spaces = 0;
|
|
111
|
+
while (source[cursor] === ' ' && spaces < 3) {
|
|
112
|
+
cursor++;
|
|
113
|
+
spaces++;
|
|
114
|
+
}
|
|
115
|
+
if (!source[cursor] || whitespace(source[cursor]))
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const newline = source.indexOf('\n', cursor);
|
|
119
|
+
let limit = newline < 0 ? source.length : newline;
|
|
120
|
+
if (source[limit - 1] === '\r')
|
|
121
|
+
limit--;
|
|
122
|
+
const target = destination(source, cursor, limit);
|
|
123
|
+
if (!target || target.range[0] === target.range[1])
|
|
124
|
+
return null;
|
|
125
|
+
cursor = target.end;
|
|
126
|
+
while (cursor < limit && whitespace(source[cursor]))
|
|
127
|
+
cursor++;
|
|
128
|
+
if (cursor === limit)
|
|
129
|
+
return target.range;
|
|
130
|
+
if (cursor === target.end)
|
|
131
|
+
return null;
|
|
132
|
+
return markdownLinkTitleEnd(source, cursor, limit, false) === null ? null : target.range;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=markdown-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-links.js","sourceRoot":"","sources":["../src/markdown-links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Format } from './types.js';
|
|
2
|
+
export interface PreservationReport {
|
|
3
|
+
/** True when nothing factual went missing and length stayed in bounds. */
|
|
4
|
+
ok: boolean;
|
|
5
|
+
missing: {
|
|
6
|
+
numbers: string[];
|
|
7
|
+
links: string[];
|
|
8
|
+
code: string[];
|
|
9
|
+
quotes: string[];
|
|
10
|
+
headings: string[];
|
|
11
|
+
};
|
|
12
|
+
/** Figures and links the revision introduced. New facts need a source, so they fail too. */
|
|
13
|
+
added: {
|
|
14
|
+
numbers: string[];
|
|
15
|
+
links: string[];
|
|
16
|
+
};
|
|
17
|
+
/** Editable body words, revised ÷ original. */
|
|
18
|
+
lengthRatio: number;
|
|
19
|
+
reasons: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Single source of truth for how much a revision may change body length, used by the brief and the check. */
|
|
22
|
+
export declare const revisionLengthTolerance = 0.15;
|
|
23
|
+
export interface PreservationOptions {
|
|
24
|
+
format?: Format;
|
|
25
|
+
/** Allowed body-length change, as a fraction. Default revisionLengthTolerance. */
|
|
26
|
+
lengthTolerance?: number;
|
|
27
|
+
/** Whether heading text may change. Default false. */
|
|
28
|
+
headingsMayChange?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deterministic check that a rewrite kept the facts. Numbers, link targets,
|
|
32
|
+
* code, quotations and headings must survive; new figures or links fail
|
|
33
|
+
* because a revision must not invent claims. It can't judge meaning, so a
|
|
34
|
+
* passing report is necessary, not sufficient.
|
|
35
|
+
*/
|
|
36
|
+
export declare function comparePreservation(original: string, revised: string, options?: PreservationOptions): PreservationReport;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.revisionLengthTolerance = void 0;
|
|
4
|
+
exports.comparePreservation = comparePreservation;
|
|
5
|
+
const document_js_1 = require("./document.js");
|
|
6
|
+
const protected_tokens_js_1 = require("./protected-tokens.js");
|
|
7
|
+
/** Single source of truth for how much a revision may change body length, used by the brief and the check. */
|
|
8
|
+
exports.revisionLengthTolerance = 0.15;
|
|
9
|
+
// Figures with their attached currency, percent or unit-free thousands separators.
|
|
10
|
+
const figure = /(?<![\p{L}\p{N}])[$£€]?\d+(?:[.,]\d+)*(?:\s?%|[kKmMbB]\b|\+)?/gu;
|
|
11
|
+
function facts(source, format) {
|
|
12
|
+
const document = (0, document_js_1.parseDocument)(source, format);
|
|
13
|
+
// The parser's own code and quotation ranges: one definition of "code" everywhere.
|
|
14
|
+
const code = document.codeRanges.map((range) => source.slice(...range).trim());
|
|
15
|
+
const quotes = document.quoteRanges.map((range) => source.slice(...range).replace(/\s+/g, ' ').trim());
|
|
16
|
+
const headings = [];
|
|
17
|
+
let body = '';
|
|
18
|
+
let words = 0;
|
|
19
|
+
for (const block of document.blocks) {
|
|
20
|
+
if (block.kind === 'heading')
|
|
21
|
+
headings.push(block.text.replace(/\s+/g, ' ').trim());
|
|
22
|
+
else if (!block.skip) {
|
|
23
|
+
body += `${block.text}\n`;
|
|
24
|
+
words += (block.text.match(/[\p{L}\p{N}]+(?:['’.-][\p{L}\p{N}]+)*/gu) ?? []).length;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const links = format === 'md' || format === 'mdx' ? (0, document_js_1.collectMarkdownLinkTargets)(source, format).map((target) => target.target)
|
|
28
|
+
: [...source.matchAll(/\bhref\s*=\s*["']([^"']+)["']/gi)].map((match) => match[1]);
|
|
29
|
+
// Plain text has no link syntax: its literal URLs and email addresses are the links.
|
|
30
|
+
// Slash-joined words ("AppFolio/Buildium", "24/7") scan as paths, so paths don't count.
|
|
31
|
+
if (format === 'text')
|
|
32
|
+
links.push(...(0, protected_tokens_js_1.protectedTokenRanges)(source).map((range) => source.slice(...range)).filter((token) => /^(?:[a-z][a-z\d+.-]*:\/\/|www\.)/i.test(token) || token.includes('@')));
|
|
33
|
+
// Figures in body prose; code and quotations are compared whole above.
|
|
34
|
+
const numbers = [...body.matchAll(figure)].map((match) => match[0].replace(/\s/g, ''));
|
|
35
|
+
return { numbers, links, code, quotes, headings, words };
|
|
36
|
+
}
|
|
37
|
+
/** Items of `from` not accounted for in `to`, respecting multiplicity. */
|
|
38
|
+
function difference(from, to) {
|
|
39
|
+
const remaining = new Map();
|
|
40
|
+
for (const item of to)
|
|
41
|
+
remaining.set(item, (remaining.get(item) ?? 0) + 1);
|
|
42
|
+
const lost = [];
|
|
43
|
+
for (const item of from) {
|
|
44
|
+
const count = remaining.get(item) ?? 0;
|
|
45
|
+
if (count)
|
|
46
|
+
remaining.set(item, count - 1);
|
|
47
|
+
else
|
|
48
|
+
lost.push(item);
|
|
49
|
+
}
|
|
50
|
+
return [...new Set(lost)];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Deterministic check that a rewrite kept the facts. Numbers, link targets,
|
|
54
|
+
* code, quotations and headings must survive; new figures or links fail
|
|
55
|
+
* because a revision must not invent claims. It can't judge meaning, so a
|
|
56
|
+
* passing report is necessary, not sufficient.
|
|
57
|
+
*/
|
|
58
|
+
function comparePreservation(original, revised, options = {}) {
|
|
59
|
+
const format = options.format ?? 'md';
|
|
60
|
+
const before = facts(original, format);
|
|
61
|
+
const after = facts(revised, format);
|
|
62
|
+
const missing = {
|
|
63
|
+
// A figure repeated for emphasis may be dropped once; losing every mention is a lost fact.
|
|
64
|
+
numbers: [...new Set(before.numbers)].filter((value) => !after.numbers.includes(value)),
|
|
65
|
+
links: difference(before.links, after.links),
|
|
66
|
+
code: difference(before.code, after.code),
|
|
67
|
+
quotes: difference(before.quotes, after.quotes),
|
|
68
|
+
headings: options.headingsMayChange ? [] : difference(before.headings, after.headings),
|
|
69
|
+
};
|
|
70
|
+
const added = {
|
|
71
|
+
numbers: [...new Set(after.numbers)].filter((value) => !before.numbers.includes(value)),
|
|
72
|
+
links: difference(after.links, before.links),
|
|
73
|
+
};
|
|
74
|
+
const lengthRatio = before.words ? after.words / before.words : after.words ? Infinity : 1;
|
|
75
|
+
const tolerance = options.lengthTolerance ?? exports.revisionLengthTolerance;
|
|
76
|
+
const reasons = [];
|
|
77
|
+
for (const [kind, items] of Object.entries(missing))
|
|
78
|
+
if (items.length)
|
|
79
|
+
reasons.push(`Missing ${kind}: ${items.slice(0, 5).map((item) => JSON.stringify(item.length > 80 ? `${item.slice(0, 77)}...` : item)).join(', ')}`);
|
|
80
|
+
for (const [kind, items] of Object.entries(added))
|
|
81
|
+
if (items.length)
|
|
82
|
+
reasons.push(`Added ${kind} not in the original: ${items.slice(0, 5).map((item) => JSON.stringify(item)).join(', ')}`);
|
|
83
|
+
if (Math.abs(lengthRatio - 1) > tolerance)
|
|
84
|
+
reasons.push(`Body length changed by ${Math.round((lengthRatio - 1) * 100)}% (limit ±${Math.round(tolerance * 100)}%).`);
|
|
85
|
+
return { ok: reasons.length === 0, missing, added, lengthRatio, reasons };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=preserve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Range } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for literal URLs, email addresses, and file paths.
|
|
4
|
+
* ASCII layout whitespace ends a token; invisible Unicode inside an identifier
|
|
5
|
+
* remains part of its value and must never be normalized by prose rules.
|
|
6
|
+
*/
|
|
7
|
+
export declare function protectedTokenRanges(source: string): Range[];
|