blun-king-cli 9.1.445 → 9.1.446
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.
|
@@ -5,6 +5,11 @@ const DEFAULT_WINDOW_WORDS = 20;
|
|
|
5
5
|
const DEFAULT_REQUIRED_OCCURRENCES = 4;
|
|
6
6
|
const DEFAULT_MAX_CHARS = 24_000;
|
|
7
7
|
const DEFAULT_CHECK_EVERY_WORDS = 24;
|
|
8
|
+
const DEFAULT_SHORT_BURST_MAX_WORDS = 8;
|
|
9
|
+
const DEFAULT_SHORT_BURST_MIN_OCCURRENCES = 8;
|
|
10
|
+
const DEFAULT_SHORT_BURST_MIN_TOTAL_WORDS = 24;
|
|
11
|
+
const DEFAULT_SHORT_BURST_MIN_WORDS = 2;
|
|
12
|
+
const DEFAULT_SHORT_BURST_SCAN_WORDS = 192;
|
|
8
13
|
|
|
9
14
|
function normalizeWords(text) {
|
|
10
15
|
return String(text)
|
|
@@ -40,16 +45,80 @@ function repeatedWindow(words, options) {
|
|
|
40
45
|
return null;
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
function equalWindow(words, left, right, width) {
|
|
49
|
+
for (let offset = 0; offset < width; offset += 1) {
|
|
50
|
+
if (words[left + offset] !== words[right + offset]) return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canCloseConsecutiveWindow(words, minWords, maxWords) {
|
|
56
|
+
const lastIndex = words.length - 1;
|
|
57
|
+
for (let windowWords = minWords; windowWords <= maxWords; windowWords += 1) {
|
|
58
|
+
const previousIndex = lastIndex - windowWords;
|
|
59
|
+
if (previousIndex >= 0 && words[lastIndex] === words[previousIndex]) return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function repeatedConsecutiveWindow(words, options) {
|
|
65
|
+
const {
|
|
66
|
+
maxWords,
|
|
67
|
+
minOccurrences,
|
|
68
|
+
minTotalWords,
|
|
69
|
+
minWords,
|
|
70
|
+
scanWords,
|
|
71
|
+
firstEnd = 1,
|
|
72
|
+
} = options;
|
|
73
|
+
const sourceStart = Math.max(0, words.length - scanWords);
|
|
74
|
+
const localFirstEnd = Math.max(sourceStart + 1, firstEnd);
|
|
75
|
+
|
|
76
|
+
for (let end = localFirstEnd; end <= words.length; end += 1) {
|
|
77
|
+
for (let windowWords = minWords; windowWords <= maxWords; windowWords += 1) {
|
|
78
|
+
const requiredOccurrences = Math.max(
|
|
79
|
+
minOccurrences,
|
|
80
|
+
Math.ceil(minTotalWords / windowWords),
|
|
81
|
+
);
|
|
82
|
+
const requiredSpan = windowWords * requiredOccurrences;
|
|
83
|
+
const start = end - requiredSpan;
|
|
84
|
+
if (start < sourceStart) continue;
|
|
85
|
+
let count = 1;
|
|
86
|
+
let cursor = start + windowWords;
|
|
87
|
+
while (cursor + windowWords <= end
|
|
88
|
+
&& equalWindow(words, start, cursor, windowWords)) {
|
|
89
|
+
count += 1;
|
|
90
|
+
cursor += windowWords;
|
|
91
|
+
}
|
|
92
|
+
if (count < requiredOccurrences) continue;
|
|
93
|
+
return {
|
|
94
|
+
kind: 'short_burst',
|
|
95
|
+
count,
|
|
96
|
+
firstWords: words.slice(start, start + windowWords).join(' '),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
43
103
|
function createLiveResponseRepetitionGuard(options = {}) {
|
|
44
104
|
const config = {
|
|
45
105
|
checkEveryWords: options.checkEveryWords ?? DEFAULT_CHECK_EVERY_WORDS,
|
|
46
106
|
maxChars: options.maxChars ?? DEFAULT_MAX_CHARS,
|
|
47
107
|
minWords: options.minWords ?? DEFAULT_MIN_WORDS,
|
|
48
108
|
requiredOccurrences: options.requiredOccurrences ?? DEFAULT_REQUIRED_OCCURRENCES,
|
|
109
|
+
shortBurstMaxWords: options.shortBurstMaxWords ?? DEFAULT_SHORT_BURST_MAX_WORDS,
|
|
110
|
+
shortBurstMinOccurrences: options.shortBurstMinOccurrences
|
|
111
|
+
?? DEFAULT_SHORT_BURST_MIN_OCCURRENCES,
|
|
112
|
+
shortBurstMinTotalWords: options.shortBurstMinTotalWords
|
|
113
|
+
?? DEFAULT_SHORT_BURST_MIN_TOTAL_WORDS,
|
|
114
|
+
shortBurstMinWords: options.shortBurstMinWords ?? DEFAULT_SHORT_BURST_MIN_WORDS,
|
|
115
|
+
shortBurstScanWords: options.shortBurstScanWords ?? DEFAULT_SHORT_BURST_SCAN_WORDS,
|
|
49
116
|
windowWords: options.windowWords ?? DEFAULT_WINDOW_WORDS,
|
|
50
117
|
};
|
|
51
118
|
let text = '';
|
|
52
119
|
let lastCheckedWords = 0;
|
|
120
|
+
let lastShortBurstLastWord = '';
|
|
121
|
+
let lastShortBurstWordCount = 0;
|
|
53
122
|
let detection = null;
|
|
54
123
|
|
|
55
124
|
return {
|
|
@@ -57,12 +126,46 @@ function createLiveResponseRepetitionGuard(options = {}) {
|
|
|
57
126
|
if (detection !== null || typeof delta !== 'string' || delta.length === 0) return detection;
|
|
58
127
|
text = `${text}${delta}`.slice(-config.maxChars);
|
|
59
128
|
const words = normalizeWords(text);
|
|
129
|
+
const previousShortBurstWordCount = lastShortBurstWordCount;
|
|
130
|
+
const lastWord = words.at(-1) ?? '';
|
|
131
|
+
const wordCountChanged = words.length !== previousShortBurstWordCount;
|
|
132
|
+
const lastWordChanged = lastWord !== lastShortBurstLastWord;
|
|
133
|
+
const firstChangedEnd = words.length > lastShortBurstWordCount
|
|
134
|
+
? Math.max(1, lastShortBurstWordCount)
|
|
135
|
+
: words.length;
|
|
136
|
+
const shouldCheckShortBurst = wordCountChanged || (lastWordChanged
|
|
137
|
+
&& canCloseConsecutiveWindow(
|
|
138
|
+
words,
|
|
139
|
+
config.shortBurstMinWords,
|
|
140
|
+
config.shortBurstMaxWords,
|
|
141
|
+
));
|
|
142
|
+
lastShortBurstLastWord = lastWord;
|
|
143
|
+
lastShortBurstWordCount = words.length;
|
|
144
|
+
if (words.length >= config.shortBurstMinTotalWords && shouldCheckShortBurst) {
|
|
145
|
+
const shortBurst = repeatedConsecutiveWindow(words, {
|
|
146
|
+
firstEnd: firstChangedEnd,
|
|
147
|
+
maxWords: config.shortBurstMaxWords,
|
|
148
|
+
minOccurrences: config.shortBurstMinOccurrences,
|
|
149
|
+
minTotalWords: config.shortBurstMinTotalWords,
|
|
150
|
+
minWords: config.shortBurstMinWords,
|
|
151
|
+
scanWords: config.shortBurstScanWords,
|
|
152
|
+
});
|
|
153
|
+
if (shortBurst !== null) {
|
|
154
|
+
detection = {
|
|
155
|
+
...shortBurst,
|
|
156
|
+
charCount: text.length,
|
|
157
|
+
wordCount: words.length,
|
|
158
|
+
};
|
|
159
|
+
return detection;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
60
162
|
if (words.length < config.minWords) return null;
|
|
61
163
|
if (words.length - lastCheckedWords < config.checkEveryWords) return null;
|
|
62
164
|
lastCheckedWords = words.length;
|
|
63
165
|
const repeated = repeatedWindow(words, config);
|
|
64
166
|
if (repeated === null) return null;
|
|
65
167
|
detection = {
|
|
168
|
+
kind: 'long_window',
|
|
66
169
|
...repeated,
|
|
67
170
|
charCount: text.length,
|
|
68
171
|
wordCount: words.length,
|
|
@@ -80,8 +183,14 @@ module.exports = {
|
|
|
80
183
|
DEFAULT_MAX_CHARS,
|
|
81
184
|
DEFAULT_MIN_WORDS,
|
|
82
185
|
DEFAULT_REQUIRED_OCCURRENCES,
|
|
186
|
+
DEFAULT_SHORT_BURST_MAX_WORDS,
|
|
187
|
+
DEFAULT_SHORT_BURST_MIN_OCCURRENCES,
|
|
188
|
+
DEFAULT_SHORT_BURST_MIN_TOTAL_WORDS,
|
|
189
|
+
DEFAULT_SHORT_BURST_MIN_WORDS,
|
|
190
|
+
DEFAULT_SHORT_BURST_SCAN_WORDS,
|
|
83
191
|
DEFAULT_WINDOW_WORDS,
|
|
84
192
|
createLiveResponseRepetitionGuard,
|
|
85
193
|
normalizeWords,
|
|
194
|
+
repeatedConsecutiveWindow,
|
|
86
195
|
repeatedWindow,
|
|
87
196
|
};
|