UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

567 lines (566 loc) • 25.7 kB
import { n as sliceUtf16Safe, t as avoidTrailingHighSurrogateBreak } from "./utf16-slice-D_ngcYKd.mjs"; import { t as formatFencedCodeBlock } from "./markdown-code-Buzx6wvi.mjs"; import { n as findCodeOwnership } from "./code-regions-CvUeKYli.mjs"; import { f as scanFenceSpans, l as findFenceSpanAt, u as isSafeFenceBreak } from "./chunk-BenoswI3.mjs"; import { i as findMarkdownTableRanges } from "./ir-DF9Xr3in.mjs"; //#region src/agents/embedded-agent-block-chunker.code.ts /** Project parser-owned code into the chunker's existing balanced-fence path. */ function prepareIndentedCode(source, context, force, maxChars) { const parsed = context + source; let ownership = /\t| {4}/u.test(parsed) ? findCodeOwnership(parsed, { includeIndentedSource: true }) : void 0; const replacements = []; const parts = []; let cursor = 0; let length = 0; if (ownership) for (const region of ownership.regions) { const code = region.indentedSource; if (!code || code.nested || region.end <= context.length) continue; const from = Math.max(0, region.start - context.length); const to = region.end - context.length; const valueStart = code.offsets[Math.max(0, context.length - region.start)] ?? 0; const value = code.value.slice(valueStart); const framed = formatFencedCodeBlock(value); const openerLength = framed.indexOf("\n") + 1; const bodyBudget = maxChars - openerLength * 2; if (bodyBudget < 1 || bodyBudget === 1 && /[\u{10000}-\u{10ffff}]/u.test(value) || code.offsets.some((offset, index) => offset - (code.offsets[index - 1] ?? 0) > bodyBudget)) continue; const open = !source.slice(to).trim(); const rendered = open && !force ? framed.slice(0, openerLength + value.length) : framed; const prefix = source.slice(cursor, from); parts.push(prefix, rendered); length += prefix.length; replacements.push({ from, to, start: length, end: length + rendered.length, bodyStart: length + openerLength, bodyEnd: length + openerLength + value.length, valueStart, regionStart: region.start - context.length, code, open }); length += rendered.length; cursor = to; if (open && !force) break; } if (!replacements.at(-1)?.open || force) parts.push(source.slice(cursor)); const text = replacements.length ? parts.join("") : source; const sourceIndex = (replacement, valueIndex) => { const offsets = replacement.code.offsets; let low = 0; let high = offsets.length; while (low < high) { const middle = low + high >>> 1; if (offsets[middle] <= valueIndex) low = middle + 1; else high = middle; } return Math.max(0, low - 1); }; return { text, startsWithCode: replacements[0]?.from === 0, toSource(index) { let delta = 0; for (const replacement of replacements) { if (index < replacement.start) break; if (index <= replacement.end) { if (index <= replacement.bodyStart) return replacement.from; if (index >= replacement.bodyEnd) return replacement.to; return replacement.regionStart + sourceIndex(replacement, replacement.valueStart + index - replacement.bodyStart); } delta += replacement.end - replacement.start - (replacement.to - replacement.from); } return index - delta; }, codeBoundary(index, minimum) { const replacement = replacements.find((entry) => entry.bodyStart < index && index < entry.bodyEnd); if (!replacement) return index; const start = Math.max(minimum - 1, replacement.bodyStart); let at = index; while (at > start + 1 && (/\s/u.test(text.charAt(at)) || /\s/u.test(text.charAt(at - 1)))) at -= 1; at = avoidTrailingHighSurrogateBreak(text, start, at); if (/\s/u.test(text.charAt(at)) || /\s/u.test(text.charAt(at - 1))) at = index; const mapped = sourceIndex(replacement, replacement.valueStart + at - replacement.bodyStart); return avoidTrailingHighSurrogateBreak(text, start, replacement.bodyStart + replacement.code.offsets[mapped] - replacement.valueStart); }, contextAt(index) { const replacement = replacements.find((entry) => entry.from < index && (index < entry.to || entry.open && index === entry.to)); if (replacement) return `${replacement.code.context}x${source.charAt(index - 1) === "\n" ? "\n" : ""}`; const at = context.length + index; const currentOwnership = ownership ??= findCodeOwnership(parsed, { includeIndentedSource: true }); for (const region of currentOwnership.regions) { const code = region.indentedSource; if (code?.nested && code.ownerStart < at && at <= region.end) { const prefixLength = code.context.length; return at < code.ownerStart + prefixLength ? code.context.slice(0, at - code.ownerStart) : `${code.context}x${source.charAt(index - 1) === "\n" ? "\n" : ""}`; } } const paragraph = currentOwnership.paragraphs?.find((entry) => entry.start < at && (at <= entry.end || !parsed.slice(entry.end, at).trim())); if (!paragraph) return ""; return `x${at > paragraph.end ? parsed.slice(paragraph.end, at) : source.charAt(index - 1) === "\n" ? "\n" : ""}`; } }; } //#endregion //#region src/agents/embedded-agent-block-chunker.tables.ts /** * A table that fits one message stays whole, like a fenced block: channel * renderers convert only complete tables, so a split leaves raw Markdown rows. */ function findUnsplittableTableSpans(source, fenceSpans, maxChars, streaming) { const parsed = findMarkdownTableRanges(source); const fits = (table) => table.end - table.start <= maxChars && isSafeFenceBreak(fenceSpans, table.start); const tables = parsed.filter(fits); if (!streaming || !source.includes("|")) return tables; const last = parsed.at(-1); if (last && !/\n[^\n]*\n/.test(source.slice(last.end))) { if (tables.at(-1) === last) last.end = source.length; return tables; } const lineStart = source.lastIndexOf("\n") + 1; const line = source.slice(lineStart); const headerStart = source.lastIndexOf("\n", lineStart - 2) + 1; const pending = { start: lineStart > 0 && /^[\s>|:-]*$/.test(line) && source.slice(headerStart, lineStart).includes("|") ? headerStart : line.includes("|") ? lineStart : source.length, end: source.length }; if (pending.start < source.length && fits(pending)) tables.push(pending); return tables; } /** * Picks the break when a capped window ends inside a table that fits: before * the table (below minChars if needed), or -1 to wait while a streaming table * exactly fills the window. Returns undefined when no such table is at the cut. */ function findTableBreakIndex(buffer, offset, windowLength, spans) { const cut = offset + windowLength; const table = spans.tables.find((span) => span.start < cut && cut <= span.end); if (!table) return; if (table.start > offset) { const tableBreak = buffer.slice(0, table.start - offset).trimEnd().length; if (tableBreak > 0 && isSafeFenceBreak(spans.fences, offset + tableBreak)) return tableBreak; } return table.start <= offset && table.end === offset + buffer.length && /^\r?\n?$/.test(buffer.slice(windowLength)) ? -1 : void 0; } //#endregion //#region src/agents/embedded-agent-block-chunker.ts /** * Splits streamed embedded-agent replies into Markdown-safe message chunks. */ function findSafeSentenceBreakIndex(text, unsafeSpans, minChars, offset = 0, openFence) { const matches = text.matchAll(/[.!?](?=\s|$)/g); let sentenceIdx = -1; for (const match of matches) { const at = match.index; if (at < minChars) continue; const candidate = at + 1; if (offset + candidate !== openFence?.end && isSafeFenceBreak(unsafeSpans, offset + candidate)) sentenceIdx = candidate; } return sentenceIdx >= minChars ? sentenceIdx : -1; } function findSafeLineBreakIndex(params) { const { text, unsafeSpans, minChars, reverse, separator, offset = 0 } = params; let index = reverse ? text.lastIndexOf(separator) : text.indexOf(separator); while (reverse ? index >= minChars : index !== -1) { for (let candidate = index; candidate < index + separator.length; candidate++) if (candidate >= minChars && isSafeFenceBreak(unsafeSpans, offset + candidate)) return candidate; index = reverse ? text.lastIndexOf(separator, index - 1) : text.indexOf(separator, index + separator.length); } return -1; } function findFenceCloseLineStart(buffer, fence, offset = 0) { const relativeFenceEnd = Math.min(buffer.length, Math.max(0, fence.end - offset)); if (relativeFenceEnd <= 0) return -1; const lastNewline = buffer.lastIndexOf("\n", relativeFenceEnd - 1); if (lastNewline < 0) return -1; const closingMarker = buffer.slice(lastNewline + 1, relativeFenceEnd).match(/^ {0,3}(`{3,}|~{3,})[ \t]*\r?$/)?.[1]; return closingMarker && closingMarker.charAt(0) === fence.marker.charAt(0) && closingMarker.length >= fence.marker.length ? lastNewline + 1 : -1; } function resolveFenceReopenLine(fence, maxChars) { const bareMarker = `${fence.indent}${fence.marker}`; if (bareMarker.length * 2 + 3 > maxChars) return; return fence.openLine.length + bareMarker.length + 3 <= maxChars ? fence.openLine : bareMarker; } var EmbeddedBlockChunker = class { #buffer = ""; #reopenPrefix = ""; #consumedLength = 0; #preparedSourceBreaks = []; #sourceBreaks = []; #sourceGeneration = 0; #sourceOffset = 0; #nextSourceBreak = 0; #bufferStartsAtLineStart = true; #codeContext = ""; #chunking; constructor(chunking) { this.#chunking = chunking; } append(text) { if (!text) return; this.#buffer += text; } /** Start a new source scope without emitting pending text. */ reset(sourceBreaks = [], sourceOffset = 0) { this.#buffer = ""; this.#reopenPrefix = ""; this.#consumedLength = 0; this.#preparedSourceBreaks = []; this.#sourceBreaks = sourceBreaks; this.#sourceGeneration += 1; this.#sourceOffset = sourceOffset; this.#nextSourceBreak = 0; this.#bufferStartsAtLineStart = true; this.#codeContext = ""; } /** UTF-16 source positions exclude synthetic fences and include skipped whitespace. */ get consumedLength() { return this.#consumedLength; } /** Prepared source boundaries are not evidence that a recipient received a chunk. */ get preparedSourceBreaks() { return this.#preparedSourceBreaks; } get sourceLength() { return this.#consumedLength + this.#buffer.length; } /** Replace pending source; the snapshot owner can attest that the consumed prefix is unchanged. */ replace(text, sourceOffset = 0, prefixRetained = false) { const pendingOffset = sourceOffset - this.#consumedLength; const next = this.#buffer.slice(0, Math.max(0, pendingOffset)) + text.slice(Math.max(0, -pendingOffset)); const changed = next !== this.#buffer; if (!next.startsWith(this.#buffer) || sourceOffset + text.length < this.#consumedLength) { this.#sourceBreaks = []; this.#nextSourceBreak = 0; } this.#buffer = next; if (sourceOffset === 0 && text.length < this.#consumedLength) { const { spans, state } = scanFenceSpans(text); const fence = state.open ? spans.at(-1) : void 0; const maxChars = Math.max(1, Math.floor(this.#chunking?.minChars ?? 1), Math.floor(this.#chunking?.maxChars ?? Infinity)); const reopenLine = fence && this.#chunking ? resolveFenceReopenLine(fence, maxChars) : void 0; this.#reopenPrefix = reopenLine ? `${reopenLine}\n` : ""; this.#codeContext = prepareIndentedCode(text, "", true, maxChars).contextAt(text.length); } const consumedLength = Math.min(this.#consumedLength, sourceOffset + text.length); if (!prefixRetained && sourceOffset === 0 && this.#codeContext && consumedLength > 0) { const maxChars = Math.max(1, Math.floor(this.#chunking?.minChars ?? 1), Math.floor(this.#chunking?.maxChars ?? Infinity)); this.#codeContext = prepareIndentedCode(text, "", true, maxChars).contextAt(consumedLength); } if (consumedLength === 0) { this.#bufferStartsAtLineStart = true; this.#codeContext = ""; } else if (consumedLength > sourceOffset) this.#bufferStartsAtLineStart = text.charAt(consumedLength - sourceOffset - 1) === "\n"; else if (consumedLength < this.#consumedLength) this.#bufferStartsAtLineStart = false; this.#consumedLength = consumedLength; while ((this.#preparedSourceBreaks.at(-1) ?? 0) > this.#consumedLength) this.#preparedSourceBreaks.pop(); return changed; } get bufferedText() { return this.#buffer ? `${this.#reopenPrefix}${this.#buffer}` : ""; } hasBuffered() { return this.#buffer.length > 0; } /** Emit safe chunks according to size and Markdown fence constraints. */ drain(params) { const sourceBreaks = this.#sourceBreaks; while (this.#nextSourceBreak < sourceBreaks.length) { const length = sourceBreaks[this.#nextSourceBreak] - this.#consumedLength; if (length <= 0) { this.#nextSourceBreak += 1; continue; } if (length > this.#buffer.length) break; const availableLength = this.bufferedText.length; const tail = this.#buffer.slice(length); this.#buffer = this.#buffer.slice(0, length); this.#drainBuffer(params, availableLength, true); if (this.#sourceBreaks !== sourceBreaks) return; const reachedBreak = this.#buffer.length === 0; this.#buffer += tail; if (!reachedBreak) return; this.#nextSourceBreak += 1; } this.#drainBuffer(params); } #drainBuffer(params, availableLength = 0, reconciledSourceBreak = false) { const { emit } = params; const sourceStart = this.#consumedLength; const preparedSourceBreaks = this.#preparedSourceBreaks; const chunking = this.#chunking; if (!this.#buffer || !params.force && !chunking) return; if (!chunking) { preparedSourceBreaks.push(sourceStart + this.#buffer.length); emit(this.bufferedText, { sourceText: this.#buffer, sourceGeneration: this.#sourceGeneration, reconciledSourceBreak: reconciledSourceBreak || void 0, sourceStart: this.#sourceOffset + this.#consumedLength, sourceEnd: this.#sourceOffset + this.#consumedLength + this.#buffer.length, startsAtLineStart: Boolean(this.#reopenPrefix) || this.#bufferStartsAtLineStart }); this.#bufferStartsAtLineStart = this.#buffer.endsWith("\n"); this.#consumedLength += this.#buffer.length; this.#buffer = ""; this.#reopenPrefix = ""; this.#codeContext = ""; return; } const minChars = Math.max(1, Math.floor(chunking.minChars ?? 1)); const maxChars = Math.max(minChars, Math.floor(chunking.maxChars ?? Infinity)); const force = params.force || availableLength >= maxChars; const originalSource = this.bufferedText; if (originalSource.length < minChars && !force) return; const indentedCode = prepareIndentedCode(originalSource, this.#codeContext, force, maxChars); let source = indentedCode.text; const startsAtLineStart = Boolean(this.#reopenPrefix) || indentedCode.startsWithCode || this.#bufferStartsAtLineStart; if (source.length < minChars && !force) return; if (force && source.length <= maxChars && !this.#reopenPrefix) { if (source.trim().length > 0) { preparedSourceBreaks.push(sourceStart + this.#buffer.length); emit(source, { sourceText: this.#buffer, sourceGeneration: this.#sourceGeneration, reconciledSourceBreak: reconciledSourceBreak || void 0, sourceStart: this.#sourceOffset + this.#consumedLength, sourceEnd: this.#sourceOffset + this.#consumedLength + this.#buffer.length, startsAtLineStart }); } this.#bufferStartsAtLineStart = this.#buffer.endsWith("\n"); this.#codeContext = indentedCode.contextAt(originalSource.length); this.#consumedLength += this.#buffer.length; this.#buffer = ""; this.#reopenPrefix = ""; return; } const { spans: fenceSpans, state: fenceState } = scanFenceSpans(source, { atLineStart: startsAtLineStart }); const openFence = fenceState.open ? fenceSpans.at(-1) : void 0; const removedFenceInfo = []; let removedFenceInfoLength = 0; for (const fence of fenceSpans) { fence.start -= removedFenceInfoLength; fence.end -= removedFenceInfoLength; const reopenFenceLine = resolveFenceReopenLine(fence, maxChars); if (!reopenFenceLine || reopenFenceLine === fence.openLine || fence.end - fence.start <= maxChars) continue; source = source.slice(0, fence.start) + reopenFenceLine + source.slice(fence.start + fence.openLine.length); const removedLength = fence.openLine.length - reopenFenceLine.length; removedFenceInfo.push({ at: fence.start + reopenFenceLine.length, length: removedLength }); fence.openLine = reopenFenceLine; fence.end -= removedLength; removedFenceInfoLength += removedLength; } const tables = findUnsplittableTableSpans(source, fenceSpans, maxChars, !force); const unsafe = tables.length > 0 ? [...fenceSpans, ...tables].toSorted((left, right) => left.start - right.start) : fenceSpans; const spans = { fences: fenceSpans, tables, unsafe }; const originalIndex = (index) => removedFenceInfo.reduce((offset, removed) => offset + (removed.at <= index ? removed.length : 0), index); const sourceOffset = (index) => Math.max(0, indentedCode.toSource(originalIndex(index)) - this.#reopenPrefix.length); let start = 0; let reopenFence; const emitSourceChunk = (chunk, from, to) => { preparedSourceBreaks.push(sourceStart + sourceOffset(to)); emit(chunk, { sourceText: this.#buffer.slice(sourceOffset(from), sourceOffset(to)), sourceGeneration: this.#sourceGeneration, reconciledSourceBreak: reconciledSourceBreak || void 0, sourceStart: this.#sourceOffset + this.#consumedLength + sourceOffset(from), sourceEnd: this.#sourceOffset + this.#consumedLength + sourceOffset(to), startsAtLineStart: Boolean(reopenFence) || (from === 0 ? startsAtLineStart : source.charAt(from - 1) === "\n") }); }; const resumedFence = this.#reopenPrefix ? fenceSpans[0] : void 0; if (resumedFence) { const closeStart = findFenceCloseLineStart(source, resumedFence); if (closeStart >= this.#reopenPrefix.length && !source.slice(this.#reopenPrefix.length, closeStart).trim()) start = skipLeadingNewlines(source, resumedFence.end); } while (start < source.length) { const reopenPrefix = reopenFence ? `${reopenFence.reopenFenceLine}\n` : ""; const remainingLength = reopenPrefix.length + (source.length - start); if (!force && remainingLength < minChars) break; if (chunking.flushOnParagraph && !force) { const paragraphBreak = findNextParagraphBreak(source, fenceSpans, start, minChars); const paragraphLimit = Math.max(1, maxChars - reopenPrefix.length); if (paragraphBreak && paragraphBreak.index - start <= paragraphLimit) { const chunk = `${reopenPrefix}${source.slice(start, paragraphBreak.index)}`; const nextStart = skipLeadingNewlines(source, paragraphBreak.index + paragraphBreak.length); if (chunk.trim().length > 0) emitSourceChunk(chunk, start, nextStart); start = nextStart; reopenFence = void 0; continue; } if (remainingLength < maxChars) break; } const view = source.slice(start); const breakResult = force && remainingLength <= maxChars ? this.#pickPreferredBreakIndex(view, unsafe, chunking, false, 1, start, openFence) : this.#pickBreakIndex(view, spans, chunking, force ? 1 : void 0, start, maxChars - reopenPrefix.length, openFence); if (breakResult.index <= 0) { if (force) { emitSourceChunk(`${reopenPrefix}${source.slice(start)}`, start, source.length); start = source.length; reopenFence = void 0; } break; } if (breakResult.fenceSplit) { const boundary = indentedCode.codeBoundary(originalIndex(start + breakResult.index), originalIndex(start + 1)); let removedLength = 0; for (const removed of removedFenceInfo) if (removed.at + removedLength <= boundary) removedLength += removed.length; breakResult.index = sliceUtf16Safe(view, 0, boundary - removedLength - start).length; } const consumed = this.#resolveBreakResult({ breakResult, reopenPrefix, source, start }); if (consumed === null) continue; if (consumed.chunk) emitSourceChunk(consumed.chunk, start, consumed.start); start = consumed.start; reopenFence = consumed.reopenFence; const nextLength = (reopenFence ? `${reopenFence.reopenFenceLine}\n`.length : 0) + (source.length - start); if (nextLength < minChars && !force) break; if (nextLength < maxChars && !force && !chunking.flushOnParagraph) break; } if (!reopenFence) start = skipLeadingNewlines(source, start); if (start === 0) return; const consumed = sourceOffset(start); if (consumed > 0) this.#bufferStartsAtLineStart = this.#buffer.charAt(consumed - 1) === "\n"; this.#codeContext = indentedCode.contextAt(consumed + this.#reopenPrefix.length); this.#consumedLength += consumed; this.#buffer = this.#buffer.slice(consumed); this.#reopenPrefix = !this.#codeContext && reopenFence ? `${reopenFence.reopenFenceLine}\n` : ""; } #resolveBreakResult(params) { const { breakResult, reopenPrefix, source, start } = params; const breakIdx = breakResult.index; if (breakIdx <= 0) return null; const absoluteBreakIdx = start + breakIdx; let rawChunk = `${reopenPrefix}${source.slice(start, absoluteBreakIdx)}`; if (rawChunk.trim().length === 0) return { start: skipLeadingNewlines(source, absoluteBreakIdx), reopenFence: void 0 }; const fenceSplit = breakResult.fenceSplit; if (fenceSplit) { const closeFence = rawChunk.endsWith("\n") ? fenceSplit.closeFenceLine : `\n${fenceSplit.closeFenceLine}`; rawChunk = `${rawChunk}${closeFence}`; if (absoluteBreakIdx === findFenceCloseLineStart(source, fenceSplit.fence)) return { chunk: rawChunk, start: skipLeadingNewlines(source, fenceSplit.fence.end) }; return { chunk: rawChunk, start: absoluteBreakIdx, reopenFence: fenceSplit }; } const nextStart = absoluteBreakIdx < source.length && /\s/.test(source.charAt(absoluteBreakIdx)) ? absoluteBreakIdx + 1 : absoluteBreakIdx; return { chunk: rawChunk, start: skipLeadingNewlines(source, nextStart), reopenFence: void 0 }; } #pickPreferredBreakIndex(buffer, unsafeSpans, chunking, reverse, minCharsOverride, offset = 0, openFence) { const minChars = Math.max(1, Math.floor(minCharsOverride ?? chunking.minChars)); if (buffer.length < minChars) return { index: -1 }; const preference = chunking.breakPreference ?? "paragraph"; if (preference === "paragraph") { const paragraphIdx = findSafeLineBreakIndex({ text: buffer, unsafeSpans, minChars, reverse, separator: "\n\n", offset }); if (paragraphIdx !== -1) return { index: paragraphIdx }; } if (preference === "paragraph" || preference === "newline") { const newlineIdx = findSafeLineBreakIndex({ text: buffer, unsafeSpans, minChars, reverse, separator: "\n", offset }); if (newlineIdx !== -1) return { index: newlineIdx }; } if (preference !== "newline") { const sentenceIdx = findSafeSentenceBreakIndex(buffer, unsafeSpans, minChars, offset, openFence); if (sentenceIdx !== -1) return { index: sentenceIdx }; } return { index: -1 }; } #pickBreakIndex(buffer, spans, chunking, minCharsOverride, offset = 0, maxCharsOverride, openFence) { const minChars = Math.max(1, Math.floor(minCharsOverride ?? chunking.minChars)); const maxChars = Math.max(1, Math.floor(maxCharsOverride ?? chunking.maxChars)); if (buffer.length < minChars) return { index: -1 }; const window = buffer.slice(0, Math.min(maxChars, buffer.length)); const preferred = this.#pickPreferredBreakIndex(window, spans.unsafe, chunking, true, minChars, offset, openFence); if (preferred.index !== -1) return preferred; if (buffer.length < maxChars) return { index: -1 }; const tableBreak = findTableBreakIndex(buffer, offset, window.length, spans); if (tableBreak !== void 0) return { index: tableBreak }; for (let i = window.length - 1; i >= minChars; i--) if (/\s/.test(window.charAt(i)) && isSafeFenceBreak(spans.unsafe, offset + i)) return { index: i }; if (buffer.length >= maxChars) { const firstCodePointWidth = (buffer.codePointAt(0) ?? 0) > 65535 ? 2 : 1; const forcedBreakIndex = sliceUtf16Safe(buffer, 0, Math.max(maxChars, firstCodePointWidth)).length; const absoluteBreakIndex = offset + forcedBreakIndex; const fence = findFenceSpanAt(spans.fences, absoluteBreakIndex) ?? (openFence?.end === absoluteBreakIndex ? openFence : void 0); if (fence) { const reopenFenceLine = resolveFenceReopenLine(fence, chunking.maxChars); if (!reopenFenceLine) return { index: forcedBreakIndex }; const closeFenceLine = `${fence.indent}${fence.marker}`; const fenceBreakIndex = sliceUtf16Safe(buffer, 0, Math.max(1, maxChars - closeFenceLine.length - 1)).length; if (fenceBreakIndex <= 0) return { index: forcedBreakIndex }; const closeFenceStart = findFenceCloseLineStart(buffer, fence, offset); return { index: closeFenceStart >= minChars && closeFenceStart <= fenceBreakIndex ? closeFenceStart : fenceBreakIndex, fenceSplit: { closeFenceLine, reopenFenceLine, fence } }; } return { index: forcedBreakIndex }; } return { index: -1 }; } }; function skipLeadingNewlines(value, start = 0) { let i = start; while (i < value.length && value[i] === "\n") i++; return i; } function findNextParagraphBreak(buffer, fenceSpans, startIndex = 0, minCharsFromStart = 1) { if (startIndex < 0) return null; const re = /\n[\t ]*\n+/g; re.lastIndex = startIndex; let match; while ((match = re.exec(buffer)) !== null) { const index = match.index; if (index - startIndex < minCharsFromStart) continue; const fence = findFenceSpanAt(fenceSpans, index); if (fence) { re.lastIndex = fence.end; continue; } return { index, length: match[0].length }; } return null; } //#endregion export { EmbeddedBlockChunker as t };