mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 03:41:36 +00:00
* feat(tooling): add noUncheckedIndexedAccess strict-ratchet lane * fix(packages): make indexed access explicit across ratchet packages Burns down all 65 noUncheckedIndexedAccess errors in the seven ratchet packages with behavior-identical restructuring (iteration, charAt/slice, regex-group guards, validated hextet tuple). net-policy invariant violations now throw instead of failing open. Adds @openclaw/normalization-core/expect (expectDefined/first/last) with subpath export and tests. memory-host-sdk fixes kept but the package stays out of the lane: it re-exports core src/** so the flag would apply transitively to all of core. * fix(lint): keep .oxlintrc.json strict-JSON parseable for extension lint wrappers
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
// Markdown Core module implements chunk text behavior.
|
|
function resolveChunkEarlyReturn(text: string, limit: number): string[] | undefined {
|
|
if (!text) {
|
|
return [];
|
|
}
|
|
if (limit <= 0) {
|
|
return [text];
|
|
}
|
|
if (text.length <= limit) {
|
|
return [text];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function scanParenAwareBreakpoints(text: string): { lastNewline: number; lastWhitespace: number } {
|
|
let lastNewline = -1;
|
|
let lastWhitespace = -1;
|
|
let depth = 0;
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
const char = text.charAt(i);
|
|
// Parenthesized spans often contain rewritten links or file references;
|
|
// avoid splitting them unless the window has no safer outside break.
|
|
if (char === "(") {
|
|
depth += 1;
|
|
continue;
|
|
}
|
|
if (char === ")" && depth > 0) {
|
|
depth -= 1;
|
|
continue;
|
|
}
|
|
if (depth !== 0) {
|
|
continue;
|
|
}
|
|
if (char === "\n") {
|
|
lastNewline = i;
|
|
} else if (/\s/.test(char)) {
|
|
lastWhitespace = i;
|
|
}
|
|
}
|
|
|
|
return { lastNewline, lastWhitespace };
|
|
}
|
|
|
|
/**
|
|
* Keeps UTF-16 chunk boundaries from separating a supplementary-plane character.
|
|
* A one-unit positive limit still needs to emit an entire surrogate pair.
|
|
*/
|
|
export function avoidTrailingHighSurrogateBreak(text: string, start: number, end: number): number {
|
|
if (
|
|
end >= text.length ||
|
|
text.charCodeAt(end - 1) < 0xd800 ||
|
|
text.charCodeAt(end - 1) > 0xdbff ||
|
|
text.charCodeAt(end) < 0xdc00 ||
|
|
text.charCodeAt(end) > 0xdfff
|
|
) {
|
|
return end;
|
|
}
|
|
return end - 1 > start ? end - 1 : end + 1;
|
|
}
|
|
|
|
/**
|
|
* Splits plain text into size-bounded chunks at readable boundaries.
|
|
*
|
|
* Returns the original text as one chunk when the limit is non-positive.
|
|
*/
|
|
export function chunkText(text: string, limit: number): string[] {
|
|
const early = resolveChunkEarlyReturn(text, limit);
|
|
if (early) {
|
|
return early;
|
|
}
|
|
|
|
const chunks: string[] = [];
|
|
let cursor = 0;
|
|
while (cursor < text.length) {
|
|
if (text.length - cursor <= limit) {
|
|
chunks.push(text.slice(cursor));
|
|
break;
|
|
}
|
|
const windowEnd = Math.min(text.length, cursor + limit);
|
|
const window = text.slice(cursor, windowEnd);
|
|
const { lastNewline, lastWhitespace } = scanParenAwareBreakpoints(window);
|
|
// Prefer block boundaries, then spaces, then a hard size cut when no
|
|
// readable breakpoint exists inside this window.
|
|
const breakOffset = lastNewline > 0 ? lastNewline : lastWhitespace;
|
|
const end = avoidTrailingHighSurrogateBreak(
|
|
text,
|
|
cursor,
|
|
breakOffset > 0 ? cursor + breakOffset : windowEnd,
|
|
);
|
|
chunks.push(text.slice(cursor, end));
|
|
cursor = end;
|
|
while (cursor < text.length && /\s/.test(text[cursor] ?? "")) {
|
|
cursor += 1;
|
|
}
|
|
}
|
|
return chunks;
|
|
}
|