openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
198 lines (197 loc) • 7.71 kB
JavaScript
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { isAlias, isMap, isNode, isScalar, parseDocument } from "yaml";
//#region packages/markdown-core/src/frontmatter.ts
function stripQuotes(value) {
const quote = value.at(0);
return (quote === "\"" || quote === "'") && value.at(-1) === quote ? value.slice(1, -1) : value;
}
function coerceYamlFrontmatterValue(value) {
if (value === null || value === void 0) return;
if (typeof value === "string") return {
value: value.trim(),
kind: "scalar"
};
if (typeof value === "number" || typeof value === "boolean") return {
value: String(value),
kind: "scalar"
};
if (typeof value === "object") try {
return {
value: JSON.stringify(value),
kind: "structured"
};
} catch {
return;
}
}
function parseLineFrontmatter(block) {
const result = {};
const lines = block.split("\n");
for (let i = 0; i < lines.length; i += 1) {
const match = lines.at(i)?.match(/^([\w-]+):\s*(.*)$/);
const key = match?.[1];
const rawValue = match?.[2];
if (!key || rawValue === void 0) continue;
let value = rawValue.trim();
if (!value && /^[ \t]/.test(lines.at(i + 1) ?? "")) {
const valueLines = [];
while (i + 1 < lines.length) {
const line = lines.at(i + 1);
if (line === void 0 || line && !/^[ \t]/.test(line)) break;
valueLines.push(line);
i += 1;
}
value = valueLines.join("\n").trim();
} else value = stripQuotes(value);
if (value) result[key] = value;
}
return result;
}
const FREEFORM_TEXT_FIELDS = /* @__PURE__ */ new Set([
"description",
"read_when",
"summary"
]);
function normalizeFreeformFieldAtError(block) {
const doc = parseDocument(block, {
schema: "core",
prettyErrors: false
});
if (!isMap(doc.contents)) return block;
const error = doc.errors.find((candidate) => candidate.pos?.[0] !== void 0);
const descriptionAlias = doc.contents.items.find((candidate) => isScalar(candidate.key) && candidate.key.value === "description" && isAlias(candidate.value));
const pos = error?.pos?.[0] ?? (isNode(descriptionAlias?.key) ? descriptionAlias.key.range?.[0] : void 0);
if (pos === void 0) return block;
const lineStart = block.lastIndexOf("\n", pos) + 1;
const lineEnd = block.indexOf("\n", pos);
const end = lineEnd === -1 ? block.length : lineEnd;
const match = block.slice(lineStart, end).match(/^(?:([\w-]+)|"([\w-]+)"|'([\w-]+)'):\s*(.*)$/);
const keyName = match?.[1] ?? match?.[2] ?? match?.[3];
const rawValue = match?.[4]?.trim();
const isTopLevelField = doc.contents.items.some((pair) => isScalar(pair.key) && pair.key.value === keyName && pair.key.range?.[0] === lineStart);
const recoverColonRichText = error?.code === "BLOCK_AS_IMPLICIT_KEY" && rawValue?.includes(": ");
if (!keyName || !rawValue || !FREEFORM_TEXT_FIELDS.has(keyName) || !isTopLevelField || keyName !== "description" && !recoverColonRichText || /^[|>](?:[1-9][+-]?|[+-][1-9]?)?$/.test(rawValue)) return block;
const replacement = `${keyName}: ${JSON.stringify(stripQuotes(rawValue))}`;
return `${block.slice(0, lineStart)}${replacement}${block.slice(end)}`;
}
function parseYamlFrontmatterOnce(block, fallback) {
try {
const doc = parseDocument(block, {
schema: "core",
prettyErrors: false
});
if (doc.errors.length > 0 || !isMap(doc.contents)) return {
frontmatter: fallback,
issues: doc.errors.length > 0 ? doc.errors.map((error) => ({
code: error.code ?? error.name,
message: error.message
})) : [{
code: "INVALID_ROOT",
message: "frontmatter must be a YAML mapping"
}]
};
const parsed = doc.toJS();
if (!isRecord(parsed)) return {
frontmatter: fallback,
issues: [{
code: "INVALID_ROOT",
message: "frontmatter must be a YAML mapping"
}]
};
const inlineColonKeys = /* @__PURE__ */ new Set();
for (const pair of doc.contents.items) {
if (!isNode(pair.key)) continue;
const start = pair.key.range?.[0];
if (start === void 0) continue;
const lineEnd = block.indexOf("\n", start);
const match = block.slice(start, lineEnd === -1 ? block.length : lineEnd).match(/^([\w-]+):\s*(.*)$/);
if (match?.[1] && match[2]?.includes(":")) inlineColonKeys.add(match[1]);
}
const result = {};
for (const [rawKey, value] of Object.entries(parsed)) {
const key = rawKey.trim();
const coerced = key ? coerceYamlFrontmatterValue(value) : void 0;
if (!coerced) continue;
const fallbackValue = Object.hasOwn(fallback, key) ? fallback[key] : void 0;
result[key] = coerced.kind === "structured" && inlineColonKeys.has(key) && fallbackValue !== void 0 ? fallbackValue : coerced.value;
}
for (const [key, value] of Object.entries(fallback)) if (!Object.hasOwn(result, key)) result[key] = value;
return {
frontmatter: result,
issues: []
};
} catch (error) {
return {
frontmatter: fallback,
issues: [{
code: "YAML_EXCEPTION",
message: error instanceof Error ? error.message : String(error)
}]
};
}
}
function parseYamlFrontmatter(block) {
const fallback = parseLineFrontmatter(block);
const parsed = parseYamlFrontmatterOnce(block, fallback);
if (parsed.issues.length === 0) return parsed;
let recoveredBlock = block;
for (let i = 0; i < FREEFORM_TEXT_FIELDS.size; i += 1) {
const next = normalizeFreeformFieldAtError(recoveredBlock);
if (next === recoveredBlock) break;
recoveredBlock = next;
const reparsed = parseYamlFrontmatterOnce(recoveredBlock, fallback);
if (reparsed.issues.length === 0) return reparsed;
}
return recoveredBlock === block ? parsed : parseYamlFrontmatterOnce(recoveredBlock, fallback);
}
function normalizeFrontmatterContent(content) {
return content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}
const FRONTMATTER_CLOSING_DELIMITER = /(?:^|\n)---[^\S\n]*(?:\n|(?![\s\S]))/;
const FRONTMATTER_OPENING_DELIMITER = /^---[^\S\n]*\n/;
function extractFrontmatterBlockFromNormalized(normalized) {
const opening = FRONTMATTER_OPENING_DELIMITER.exec(normalized);
if (!opening) return;
const blockStart = opening[0].length;
const tail = normalized.slice(blockStart);
const closing = FRONTMATTER_CLOSING_DELIMITER.exec(tail);
if (!closing) return;
return {
block: tail.slice(0, closing.index),
body: tail.slice(closing.index + closing[0].length)
};
}
/** Splits a complete leading YAML frontmatter block from its Markdown body. */
function extractFrontmatterBlock(content) {
return extractFrontmatterBlockFromNormalized(normalizeFrontmatterContent(content));
}
/** Removes a leading YAML frontmatter block and returns the remaining Markdown body. */
function stripFrontmatterBlock(content) {
const normalized = normalizeFrontmatterContent(content);
return (extractFrontmatterBlockFromNormalized(normalized)?.body ?? normalized).trim();
}
/** Parses leading YAML frontmatter into string values used by skill and metadata loaders. */
function parseFrontmatterBlock(content) {
return parseFrontmatterBlockResult(content).frontmatter;
}
/** Parses frontmatter once while retaining recoverable YAML parser issues for owning loaders. */
function parseFrontmatterBlockResult(content) {
const normalized = normalizeFrontmatterContent(content);
const block = extractFrontmatterBlockFromNormalized(normalized)?.block;
if (block !== void 0) return block ? parseYamlFrontmatter(block) : {
frontmatter: {},
issues: []
};
return FRONTMATTER_OPENING_DELIMITER.test(normalized) ? {
frontmatter: {},
issues: [{
code: "UNTERMINATED_FRONTMATTER",
message: "missing closing --- delimiter"
}]
} : {
frontmatter: {},
issues: []
};
}
//#endregion
export { stripFrontmatterBlock as i, parseFrontmatterBlock as n, parseFrontmatterBlockResult as r, extractFrontmatterBlock as t };