node-karin
Version:
Lightweight, efficient, concise, and stable robot framework.
1,552 lines • 353 kB
JavaScript
#!/usr/bin/env node
import fs, { createReadStream, createWriteStream } from "node:fs";
import { fileURLToPath, URL as URL$1, pathToFileURL } from "node:url";
import path, { join } from "node:path";
import require$$1, { execSync as execSync$1, exec as exec$2, spawn } from "node:child_process";
import { pipeline } from "node:stream/promises";
import fs$1 from "fs";
import require$$0 from "node:events";
import require$$4 from "node:process";
const execSync = (cmd, options = {}) => {
try {
const result = execSync$1(cmd, options);
return { status: true, error: null, stdout: result.toString(), stderr: "" };
} catch (error2) {
return { status: false, error: error2, stdout: "", stderr: "" };
}
};
const exec$1 = (cmd, options = {}) => {
return new Promise((resolve) => {
exec$2(cmd, options, (error2, stdout, stderr) => {
const status = !error2;
resolve({ status, error: error2, stdout, stderr });
});
});
};
const pm2Dir = path.join(process.cwd(), "@karinjs/config/pm2.json");
const readPm2Config = () => {
if (!fs.existsSync(pm2Dir)) {
console.log(`[pm2] 配置文件不存在 请检查 ${pm2Dir} 是否存在`);
return null;
}
return JSON.parse(fs.readFileSync(pm2Dir, "utf-8"));
};
const ensureLogDir = (dirPath) => {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
console.log(`[pm2] 创建日志目录: ${dirPath}`);
return true;
}
return false;
};
const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
};
const rotateLogFile = async (logPath, logDirPath, logType, maxSizeMB) => {
if (!fs.existsSync(logPath)) return false;
const stats = fs.statSync(logPath);
const maxSizeBytes = maxSizeMB * 1024 * 1024;
const minKeepSizeBytes = 2 * 1024 * 1024;
console.log(`[pm2] 检查${logType}日志文件: ${logPath}`);
console.log(`[pm2] 当前大小: ${formatBytes(stats.size)}, 切割阈值: ${formatBytes(maxSizeBytes)}`);
if (stats.size < maxSizeBytes) {
console.log(`[pm2] ${logType}日志文件大小未超过阈值,无需切割
`);
return false;
}
const fileName = path.basename(logPath);
const timestamp2 = Date.now();
const archivedName = `${fileName}.${timestamp2}`;
const archivedPath = path.join(logDirPath, archivedName);
console.log(`
[pm2] 准备切割${logType}日志文件:`);
console.log(`[pm2] - 源文件: ${logPath}`);
console.log(`[pm2] - 归档文件名称: ${archivedName}`);
console.log(`[pm2] - 归档文件路径: ${archivedPath}`);
try {
const sourceStream = createReadStream(logPath);
const destStream = createWriteStream(archivedPath);
await pipeline(sourceStream, destStream);
const originalMode = fs.statSync(logPath).mode;
const fileSize = stats.size;
const keepSize = Math.max(
minKeepSizeBytes,
Math.floor(fileSize / 5)
// 保留原大小的1/5或至少2MB
);
console.log(`[pm2] 将保留${formatBytes(keepSize)}内容到新的日志文件中`);
const keepBuffer = Buffer.alloc(keepSize);
const fd = fs.openSync(logPath, "r");
fs.readSync(fd, keepBuffer, 0, keepSize, fileSize - keepSize);
fs.closeSync(fd);
fs.writeFileSync(logPath, keepBuffer);
fs.chmodSync(logPath, originalMode);
console.log(`[pm2] 日志已按大小切割: ${logType} => ${archivedName}`);
console.log(`[pm2] 原始文件大小: ${formatBytes(stats.size)}, 归档后保留: ${formatBytes(keepSize)}`);
console.log(`[pm2] 归档文件完整路径: ${archivedPath}`);
return true;
} catch (err) {
console.error(`[pm2] 切割日志失败: ${logType}`, err);
return false;
}
};
const cleanupLogFiles = (errorLogBaseName, outLogBaseName, logDirPath, maxLogDays) => {
if (maxLogDays <= 0) {
console.log(`[pm2] 日志保留天数设置为${maxLogDays},将保留所有日志文件`);
return;
}
const files = fs.readdirSync(logDirPath);
const cleanupByLogType = (baseName, logType) => {
const filePattern = new RegExp(`^${baseName.replace(/\./g, "\\.")}\\.(\\d+)$`);
const logFiles = files.filter((file) => filePattern.test(file)).map((file) => {
const match = file.match(filePattern);
return {
file,
timestamp: match ? parseInt(match[1], 10) : 0
};
}).sort((a, b) => b.timestamp - a.timestamp);
const now = Date.now();
const maxAgeMs = maxLogDays * 24 * 60 * 60 * 1e3;
let deletedCount = 0;
logFiles.forEach((item) => {
if (now - item.timestamp > maxAgeMs) {
const filePath = path.join(logDirPath, item.file);
fs.unlinkSync(filePath);
const fileDate = new Date(item.timestamp).toISOString().split("T")[0];
deletedCount++;
console.log(`[pm2] 删除过期${logType}日志: ${item.file} (${fileDate})`);
}
});
if (deletedCount > 0) {
console.log(`[pm2] 共删除${deletedCount}个超过${maxLogDays}天的${logType}日志文件`);
}
};
cleanupByLogType(errorLogBaseName, "错误");
cleanupByLogType(outLogBaseName, "输出");
};
const rotateLogs = async () => {
const config = readPm2Config();
if (!config) return;
try {
let maxLogDays = Number(config.maxLogDays) ?? 14;
let maxErrorLogSize = Number(config.maxErrorLogSize);
let maxOutLogSize = Number(config.maxOutLogSize);
if (isNaN(maxLogDays) || maxLogDays < 0) maxLogDays = 14;
if (isNaN(maxErrorLogSize) || maxErrorLogSize < 1) maxErrorLogSize = 50;
if (isNaN(maxOutLogSize) || maxOutLogSize < 1) maxOutLogSize = 50;
if (maxLogDays === 0) {
console.log("[pm2] 日志保留策略: 保留所有日志,不进行清理");
} else {
console.log(`[pm2] 日志保留策略: 最长${maxLogDays}天`);
}
const errorLogPath = path.resolve(process.cwd(), config.apps[0].error_file);
const outLogPath = path.resolve(process.cwd(), config.apps[0].out_file);
const logDirPath = path.dirname(errorLogPath);
if (ensureLogDir(logDirPath)) return;
const errorLogBaseName = path.basename(errorLogPath);
const outLogBaseName = path.basename(outLogPath);
await rotateLogFile(outLogPath, logDirPath, "输出", maxOutLogSize);
await rotateLogFile(errorLogPath, logDirPath, "错误", maxErrorLogSize);
cleanupLogFiles(errorLogBaseName, outLogBaseName, logDirPath, maxLogDays);
} catch (error2) {
console.error("[pm2] 日志切割过程中发生错误:", error2);
}
};
const upgradeScriptPath = (config) => {
const script = "index.mjs";
if (config.apps[0].script !== script) {
config.apps[0].script = script;
fs.writeFileSync(pm2Dir, JSON.stringify(config, null, 2));
}
};
const checkInitialization = () => {
const script = "index.mjs";
if (!fs.existsSync(script)) {
console.log("正在升级到1.8.0版本...");
execSync("npx ki init", { cwd: process.cwd() });
console.log("升级成功 正在启动pm2服务...");
}
};
const start$1 = async () => {
rotateLogs();
console.log("[pm2] 启动中...");
const config = readPm2Config();
if (!config) {
process.exit(1);
}
upgradeScriptPath(config);
checkInitialization();
const { error: error2 } = execSync(`pm2 start ${pm2Dir}`, { cwd: process.cwd() });
if (error2) {
console.log("[pm2] 启动失败");
console.log(error2);
process.exit(1);
}
console.log("[pm2] 启动成功");
console.log("[pm2] 重启服务: pnpm rs");
console.log("[pm2] 查看日志: pnpm log");
console.log("[pm2] 停止服务: pnpm stop");
console.log("[pm2] 查看监控: pm2 monit");
console.log("[pm2] 查看列表: pm2 list");
process.exit(0);
};
const log = async () => {
rotateLogs();
const config = readPm2Config();
if (!config) {
console.log("[pm2] 如果是新项目,请先前台启动生成配置文件: pnpm app");
process.exit(1);
}
try {
const prefix = process.platform === "win32" ? "pm2.cmd" : "pm2";
spawn(prefix, ["logs", config.apps[0].name, "--lines", config.lines || 1e3], { stdio: "inherit", shell: true });
} catch (error2) {
console.error("[pm2] 发生未知错误: 请检查pm2是否安装 【npm install -g pm2】");
console.error(error2);
process.exit(1);
}
};
const stop = async () => {
rotateLogs();
const config = readPm2Config();
if (!config) {
console.log("[pm2] 如果是新项目,请先前台启动生成配置文件: pnpm app");
process.exit(1);
}
execSync(`pm2 stop ${config.apps[0].name}`, { cwd: process.cwd() });
console.log("[pm2] 停止成功");
process.exit(0);
};
const restart = async (force) => {
rotateLogs();
try {
const forceRestart = () => {
const appName2 = readPm2Config()?.apps?.[0]?.name || "karin";
execSync(`pm2 delete ${appName2}`, { cwd: process.cwd() });
execSync(`pm2 start ${pm2Dir}`, { cwd: process.cwd() });
console.log("[pm2] 重启成功");
};
console.log("[pm2] 重启中...");
if (!fs.existsSync(pm2Dir)) {
console.log(`[pm2] 配置文件不存在 请检查 ${pm2Dir} 是否存在`);
console.log("[pm2] 如果是新项目,请先前台启动生成配置文件: pnpm app");
process.exit(1);
}
if (force) {
forceRestart();
process.exit(0);
}
const appName = readPm2Config()?.apps?.[0]?.name || "karin";
if (!appName) ;
execSync(`pm2 restart ${appName}`, { cwd: process.cwd() });
console.log("[pm2] 重启成功");
process.exit(0);
} catch (error2) {
console.log("[pm2] 尝试直接重启失败 尝试删除服务并重新启动");
const appName = readPm2Config()?.apps?.[0]?.name || "karin";
execSync(`pm2 delete ${appName}`, { cwd: process.cwd() });
execSync(`pm2 start ${pm2Dir}`, { cwd: process.cwd() });
console.log("[pm2] 重启成功");
process.exit(0);
}
};
const pm2 = {
start: start$1,
log,
stop,
restart
};
const ALIAS = Symbol.for("yaml.alias");
const DOC = Symbol.for("yaml.document");
const MAP = Symbol.for("yaml.map");
const PAIR = Symbol.for("yaml.pair");
const SCALAR$1 = Symbol.for("yaml.scalar");
const SEQ = Symbol.for("yaml.seq");
const NODE_TYPE = Symbol.for("yaml.node.type");
const isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS;
const isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC;
const isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP;
const isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR;
const isScalar$1 = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR$1;
const isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ;
function isCollection$1(node) {
if (node && typeof node === "object")
switch (node[NODE_TYPE]) {
case MAP:
case SEQ:
return true;
}
return false;
}
function isNode(node) {
if (node && typeof node === "object")
switch (node[NODE_TYPE]) {
case ALIAS:
case MAP:
case SCALAR$1:
case SEQ:
return true;
}
return false;
}
const hasAnchor = (node) => (isScalar$1(node) || isCollection$1(node)) && !!node.anchor;
const BREAK$1 = Symbol("break visit");
const SKIP$1 = Symbol("skip children");
const REMOVE$1 = Symbol("remove node");
function visit$1(node, visitor) {
const visitor_ = initVisitor(visitor);
if (isDocument(node)) {
const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));
if (cd === REMOVE$1)
node.contents = null;
} else
visit_(null, node, visitor_, Object.freeze([]));
}
visit$1.BREAK = BREAK$1;
visit$1.SKIP = SKIP$1;
visit$1.REMOVE = REMOVE$1;
function visit_(key, node, visitor, path2) {
const ctrl = callVisitor(key, node, visitor, path2);
if (isNode(ctrl) || isPair(ctrl)) {
replaceNode(key, path2, ctrl);
return visit_(key, ctrl, visitor, path2);
}
if (typeof ctrl !== "symbol") {
if (isCollection$1(node)) {
path2 = Object.freeze(path2.concat(node));
for (let i = 0; i < node.items.length; ++i) {
const ci = visit_(i, node.items[i], visitor, path2);
if (typeof ci === "number")
i = ci - 1;
else if (ci === BREAK$1)
return BREAK$1;
else if (ci === REMOVE$1) {
node.items.splice(i, 1);
i -= 1;
}
}
} else if (isPair(node)) {
path2 = Object.freeze(path2.concat(node));
const ck = visit_("key", node.key, visitor, path2);
if (ck === BREAK$1)
return BREAK$1;
else if (ck === REMOVE$1)
node.key = null;
const cv = visit_("value", node.value, visitor, path2);
if (cv === BREAK$1)
return BREAK$1;
else if (cv === REMOVE$1)
node.value = null;
}
}
return ctrl;
}
async function visitAsync(node, visitor) {
const visitor_ = initVisitor(visitor);
if (isDocument(node)) {
const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node]));
if (cd === REMOVE$1)
node.contents = null;
} else
await visitAsync_(null, node, visitor_, Object.freeze([]));
}
visitAsync.BREAK = BREAK$1;
visitAsync.SKIP = SKIP$1;
visitAsync.REMOVE = REMOVE$1;
async function visitAsync_(key, node, visitor, path2) {
const ctrl = await callVisitor(key, node, visitor, path2);
if (isNode(ctrl) || isPair(ctrl)) {
replaceNode(key, path2, ctrl);
return visitAsync_(key, ctrl, visitor, path2);
}
if (typeof ctrl !== "symbol") {
if (isCollection$1(node)) {
path2 = Object.freeze(path2.concat(node));
for (let i = 0; i < node.items.length; ++i) {
const ci = await visitAsync_(i, node.items[i], visitor, path2);
if (typeof ci === "number")
i = ci - 1;
else if (ci === BREAK$1)
return BREAK$1;
else if (ci === REMOVE$1) {
node.items.splice(i, 1);
i -= 1;
}
}
} else if (isPair(node)) {
path2 = Object.freeze(path2.concat(node));
const ck = await visitAsync_("key", node.key, visitor, path2);
if (ck === BREAK$1)
return BREAK$1;
else if (ck === REMOVE$1)
node.key = null;
const cv = await visitAsync_("value", node.value, visitor, path2);
if (cv === BREAK$1)
return BREAK$1;
else if (cv === REMOVE$1)
node.value = null;
}
}
return ctrl;
}
function initVisitor(visitor) {
if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) {
return Object.assign({
Alias: visitor.Node,
Map: visitor.Node,
Scalar: visitor.Node,
Seq: visitor.Node
}, visitor.Value && {
Map: visitor.Value,
Scalar: visitor.Value,
Seq: visitor.Value
}, visitor.Collection && {
Map: visitor.Collection,
Seq: visitor.Collection
}, visitor);
}
return visitor;
}
function callVisitor(key, node, visitor, path2) {
if (typeof visitor === "function")
return visitor(key, node, path2);
if (isMap(node))
return visitor.Map?.(key, node, path2);
if (isSeq(node))
return visitor.Seq?.(key, node, path2);
if (isPair(node))
return visitor.Pair?.(key, node, path2);
if (isScalar$1(node))
return visitor.Scalar?.(key, node, path2);
if (isAlias(node))
return visitor.Alias?.(key, node, path2);
return void 0;
}
function replaceNode(key, path2, node) {
const parent = path2[path2.length - 1];
if (isCollection$1(parent)) {
parent.items[key] = node;
} else if (isPair(parent)) {
if (key === "key")
parent.key = node;
else
parent.value = node;
} else if (isDocument(parent)) {
parent.contents = node;
} else {
const pt = isAlias(parent) ? "alias" : "scalar";
throw new Error(`Cannot replace node with ${pt} parent`);
}
}
const escapeChars = {
"!": "%21",
",": "%2C",
"[": "%5B",
"]": "%5D",
"{": "%7B",
"}": "%7D"
};
const escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]);
class Directives {
constructor(yaml, tags) {
this.docStart = null;
this.docEnd = false;
this.yaml = Object.assign({}, Directives.defaultYaml, yaml);
this.tags = Object.assign({}, Directives.defaultTags, tags);
}
clone() {
const copy = new Directives(this.yaml, this.tags);
copy.docStart = this.docStart;
return copy;
}
/**
* During parsing, get a Directives instance for the current document and
* update the stream state according to the current version's spec.
*/
atDocument() {
const res = new Directives(this.yaml, this.tags);
switch (this.yaml.version) {
case "1.1":
this.atNextDocument = true;
break;
case "1.2":
this.atNextDocument = false;
this.yaml = {
explicit: Directives.defaultYaml.explicit,
version: "1.2"
};
this.tags = Object.assign({}, Directives.defaultTags);
break;
}
return res;
}
/**
* @param onError - May be called even if the action was successful
* @returns `true` on success
*/
add(line, onError) {
if (this.atNextDocument) {
this.yaml = { explicit: Directives.defaultYaml.explicit, version: "1.1" };
this.tags = Object.assign({}, Directives.defaultTags);
this.atNextDocument = false;
}
const parts = line.trim().split(/[ \t]+/);
const name = parts.shift();
switch (name) {
case "%TAG": {
if (parts.length !== 2) {
onError(0, "%TAG directive should contain exactly two parts");
if (parts.length < 2)
return false;
}
const [handle, prefix] = parts;
this.tags[handle] = prefix;
return true;
}
case "%YAML": {
this.yaml.explicit = true;
if (parts.length !== 1) {
onError(0, "%YAML directive should contain exactly one part");
return false;
}
const [version] = parts;
if (version === "1.1" || version === "1.2") {
this.yaml.version = version;
return true;
} else {
const isValid = /^\d+\.\d+$/.test(version);
onError(6, `Unsupported YAML version ${version}`, isValid);
return false;
}
}
default:
onError(0, `Unknown directive ${name}`, true);
return false;
}
}
/**
* Resolves a tag, matching handles to those defined in %TAG directives.
*
* @returns Resolved tag, which may also be the non-specific tag `'!'` or a
* `'!local'` tag, or `null` if unresolvable.
*/
tagName(source, onError) {
if (source === "!")
return "!";
if (source[0] !== "!") {
onError(`Not a valid tag: ${source}`);
return null;
}
if (source[1] === "<") {
const verbatim = source.slice(2, -1);
if (verbatim === "!" || verbatim === "!!") {
onError(`Verbatim tags aren't resolved, so ${source} is invalid.`);
return null;
}
if (source[source.length - 1] !== ">")
onError("Verbatim tags must end with a >");
return verbatim;
}
const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s);
if (!suffix)
onError(`The ${source} tag has no suffix`);
const prefix = this.tags[handle];
if (prefix) {
try {
return prefix + decodeURIComponent(suffix);
} catch (error2) {
onError(String(error2));
return null;
}
}
if (handle === "!")
return source;
onError(`Could not resolve tag: ${source}`);
return null;
}
/**
* Given a fully resolved tag, returns its printable string form,
* taking into account current tag prefixes and defaults.
*/
tagString(tag) {
for (const [handle, prefix] of Object.entries(this.tags)) {
if (tag.startsWith(prefix))
return handle + escapeTagName(tag.substring(prefix.length));
}
return tag[0] === "!" ? tag : `!<${tag}>`;
}
toString(doc) {
const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : [];
const tagEntries = Object.entries(this.tags);
let tagNames;
if (doc && tagEntries.length > 0 && isNode(doc.contents)) {
const tags = {};
visit$1(doc.contents, (_key, node) => {
if (isNode(node) && node.tag)
tags[node.tag] = true;
});
tagNames = Object.keys(tags);
} else
tagNames = [];
for (const [handle, prefix] of tagEntries) {
if (handle === "!!" && prefix === "tag:yaml.org,2002:")
continue;
if (!doc || tagNames.some((tn) => tn.startsWith(prefix)))
lines.push(`%TAG ${handle} ${prefix}`);
}
return lines.join("\n");
}
}
Directives.defaultYaml = { explicit: false, version: "1.2" };
Directives.defaultTags = { "!!": "tag:yaml.org,2002:" };
function anchorIsValid(anchor) {
if (/[\x00-\x19\s,[\]{}]/.test(anchor)) {
const sa = JSON.stringify(anchor);
const msg = `Anchor must not contain whitespace or control characters: ${sa}`;
throw new Error(msg);
}
return true;
}
function anchorNames(root) {
const anchors = /* @__PURE__ */ new Set();
visit$1(root, {
Value(_key, node) {
if (node.anchor)
anchors.add(node.anchor);
}
});
return anchors;
}
function findNewAnchor(prefix, exclude) {
for (let i = 1; true; ++i) {
const name = `${prefix}${i}`;
if (!exclude.has(name))
return name;
}
}
function createNodeAnchors(doc, prefix) {
const aliasObjects = [];
const sourceObjects = /* @__PURE__ */ new Map();
let prevAnchors = null;
return {
onAnchor: (source) => {
aliasObjects.push(source);
if (!prevAnchors)
prevAnchors = anchorNames(doc);
const anchor = findNewAnchor(prefix, prevAnchors);
prevAnchors.add(anchor);
return anchor;
},
/**
* With circular references, the source node is only resolved after all
* of its child nodes are. This is why anchors are set only after all of
* the nodes have been created.
*/
setAnchors: () => {
for (const source of aliasObjects) {
const ref = sourceObjects.get(source);
if (typeof ref === "object" && ref.anchor && (isScalar$1(ref.node) || isCollection$1(ref.node))) {
ref.node.anchor = ref.anchor;
} else {
const error2 = new Error("Failed to resolve repeated object (this should not happen)");
error2.source = source;
throw error2;
}
}
},
sourceObjects
};
}
function applyReviver(reviver, obj, key, val) {
if (val && typeof val === "object") {
if (Array.isArray(val)) {
for (let i = 0, len = val.length; i < len; ++i) {
const v0 = val[i];
const v1 = applyReviver(reviver, val, String(i), v0);
if (v1 === void 0)
delete val[i];
else if (v1 !== v0)
val[i] = v1;
}
} else if (val instanceof Map) {
for (const k of Array.from(val.keys())) {
const v0 = val.get(k);
const v1 = applyReviver(reviver, val, k, v0);
if (v1 === void 0)
val.delete(k);
else if (v1 !== v0)
val.set(k, v1);
}
} else if (val instanceof Set) {
for (const v0 of Array.from(val)) {
const v1 = applyReviver(reviver, val, v0, v0);
if (v1 === void 0)
val.delete(v0);
else if (v1 !== v0) {
val.delete(v0);
val.add(v1);
}
}
} else {
for (const [k, v0] of Object.entries(val)) {
const v1 = applyReviver(reviver, val, k, v0);
if (v1 === void 0)
delete val[k];
else if (v1 !== v0)
val[k] = v1;
}
}
}
return reviver.call(obj, key, val);
}
function toJS(value, arg, ctx) {
if (Array.isArray(value))
return value.map((v, i) => toJS(v, String(i), ctx));
if (value && typeof value.toJSON === "function") {
if (!ctx || !hasAnchor(value))
return value.toJSON(arg, ctx);
const data = { aliasCount: 0, count: 1, res: void 0 };
ctx.anchors.set(value, data);
ctx.onCreate = (res2) => {
data.res = res2;
delete ctx.onCreate;
};
const res = value.toJSON(arg, ctx);
if (ctx.onCreate)
ctx.onCreate(res);
return res;
}
if (typeof value === "bigint" && !ctx?.keep)
return Number(value);
return value;
}
class NodeBase {
constructor(type) {
Object.defineProperty(this, NODE_TYPE, { value: type });
}
/** Create a copy of this node. */
clone() {
const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
if (this.range)
copy.range = this.range.slice();
return copy;
}
/** A plain JavaScript representation of this node. */
toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
if (!isDocument(doc))
throw new TypeError("A document argument is required");
const ctx = {
anchors: /* @__PURE__ */ new Map(),
doc,
keep: true,
mapAsMap: mapAsMap === true,
mapKeyWarned: false,
maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100
};
const res = toJS(this, "", ctx);
if (typeof onAnchor === "function")
for (const { count, res: res2 } of ctx.anchors.values())
onAnchor(res2, count);
return typeof reviver === "function" ? applyReviver(reviver, { "": res }, "", res) : res;
}
}
class Alias extends NodeBase {
constructor(source) {
super(ALIAS);
this.source = source;
Object.defineProperty(this, "tag", {
set() {
throw new Error("Alias nodes cannot have tags");
}
});
}
/**
* Resolve the value of this alias within `doc`, finding the last
* instance of the `source` anchor before this node.
*/
resolve(doc) {
let found = void 0;
visit$1(doc, {
Node: (_key, node) => {
if (node === this)
return visit$1.BREAK;
if (node.anchor === this.source)
found = node;
}
});
return found;
}
toJSON(_arg, ctx) {
if (!ctx)
return { source: this.source };
const { anchors, doc, maxAliasCount } = ctx;
const source = this.resolve(doc);
if (!source) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
throw new ReferenceError(msg);
}
let data = anchors.get(source);
if (!data) {
toJS(source, null, ctx);
data = anchors.get(source);
}
if (!data || data.res === void 0) {
const msg = "This should not happen: Alias anchor was not resolved?";
throw new ReferenceError(msg);
}
if (maxAliasCount >= 0) {
data.count += 1;
if (data.aliasCount === 0)
data.aliasCount = getAliasCount(doc, source, anchors);
if (data.count * data.aliasCount > maxAliasCount) {
const msg = "Excessive alias count indicates a resource exhaustion attack";
throw new ReferenceError(msg);
}
}
return data.res;
}
toString(ctx, _onComment, _onChompKeep) {
const src = `*${this.source}`;
if (ctx) {
anchorIsValid(this.source);
if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;
throw new Error(msg);
}
if (ctx.implicitKey)
return `${src} `;
}
return src;
}
}
function getAliasCount(doc, node, anchors) {
if (isAlias(node)) {
const source = node.resolve(doc);
const anchor = anchors && source && anchors.get(source);
return anchor ? anchor.count * anchor.aliasCount : 0;
} else if (isCollection$1(node)) {
let count = 0;
for (const item of node.items) {
const c = getAliasCount(doc, item, anchors);
if (c > count)
count = c;
}
return count;
} else if (isPair(node)) {
const kc = getAliasCount(doc, node.key, anchors);
const vc = getAliasCount(doc, node.value, anchors);
return Math.max(kc, vc);
}
return 1;
}
const isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object";
class Scalar extends NodeBase {
constructor(value) {
super(SCALAR$1);
this.value = value;
}
toJSON(arg, ctx) {
return ctx?.keep ? this.value : toJS(this.value, arg, ctx);
}
toString() {
return String(this.value);
}
}
Scalar.BLOCK_FOLDED = "BLOCK_FOLDED";
Scalar.BLOCK_LITERAL = "BLOCK_LITERAL";
Scalar.PLAIN = "PLAIN";
Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE";
Scalar.QUOTE_SINGLE = "QUOTE_SINGLE";
const defaultTagPrefix = "tag:yaml.org,2002:";
function findTagObject(value, tagName, tags) {
if (tagName) {
const match = tags.filter((t) => t.tag === tagName);
const tagObj = match.find((t) => !t.format) ?? match[0];
if (!tagObj)
throw new Error(`Tag ${tagName} not found`);
return tagObj;
}
return tags.find((t) => t.identify?.(value) && !t.format);
}
function createNode(value, tagName, ctx) {
if (isDocument(value))
value = value.contents;
if (isNode(value))
return value;
if (isPair(value)) {
const map2 = ctx.schema[MAP].createNode?.(ctx.schema, null, ctx);
map2.items.push(value);
return map2;
}
if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) {
value = value.valueOf();
}
const { aliasDuplicateObjects, onAnchor, onTagObj, schema: schema2, sourceObjects } = ctx;
let ref = void 0;
if (aliasDuplicateObjects && value && typeof value === "object") {
ref = sourceObjects.get(value);
if (ref) {
if (!ref.anchor)
ref.anchor = onAnchor(value);
return new Alias(ref.anchor);
} else {
ref = { anchor: null, node: null };
sourceObjects.set(value, ref);
}
}
if (tagName?.startsWith("!!"))
tagName = defaultTagPrefix + tagName.slice(2);
let tagObj = findTagObject(value, tagName, schema2.tags);
if (!tagObj) {
if (value && typeof value.toJSON === "function") {
value = value.toJSON();
}
if (!value || typeof value !== "object") {
const node2 = new Scalar(value);
if (ref)
ref.node = node2;
return node2;
}
tagObj = value instanceof Map ? schema2[MAP] : Symbol.iterator in Object(value) ? schema2[SEQ] : schema2[MAP];
}
if (onTagObj) {
onTagObj(tagObj);
delete ctx.onTagObj;
}
const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar(value);
if (tagName)
node.tag = tagName;
else if (!tagObj.default)
node.tag = tagObj.tag;
if (ref)
ref.node = node;
return node;
}
function collectionFromPath(schema2, path2, value) {
let v = value;
for (let i = path2.length - 1; i >= 0; --i) {
const k = path2[i];
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
const a = [];
a[k] = v;
v = a;
} else {
v = /* @__PURE__ */ new Map([[k, v]]);
}
}
return createNode(v, void 0, {
aliasDuplicateObjects: false,
keepUndefined: false,
onAnchor: () => {
throw new Error("This should not happen, please report a bug.");
},
schema: schema2,
sourceObjects: /* @__PURE__ */ new Map()
});
}
const isEmptyPath = (path2) => path2 == null || typeof path2 === "object" && !!path2[Symbol.iterator]().next().done;
class Collection extends NodeBase {
constructor(type, schema2) {
super(type);
Object.defineProperty(this, "schema", {
value: schema2,
configurable: true,
enumerable: false,
writable: true
});
}
/**
* Create a copy of this collection.
*
* @param schema - If defined, overwrites the original's schema
*/
clone(schema2) {
const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
if (schema2)
copy.schema = schema2;
copy.items = copy.items.map((it) => isNode(it) || isPair(it) ? it.clone(schema2) : it);
if (this.range)
copy.range = this.range.slice();
return copy;
}
/**
* Adds a value to the collection. For `!!map` and `!!omap` the value must
* be a Pair instance or a `{ key, value }` object, which may not have a key
* that already exists in the map.
*/
addIn(path2, value) {
if (isEmptyPath(path2))
this.add(value);
else {
const [key, ...rest] = path2;
const node = this.get(key, true);
if (isCollection$1(node))
node.addIn(rest, value);
else if (node === void 0 && this.schema)
this.set(key, collectionFromPath(this.schema, rest, value));
else
throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
}
}
/**
* Removes a value from the collection.
* @returns `true` if the item was found and removed.
*/
deleteIn(path2) {
const [key, ...rest] = path2;
if (rest.length === 0)
return this.delete(key);
const node = this.get(key, true);
if (isCollection$1(node))
return node.deleteIn(rest);
else
throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
}
/**
* Returns item at `key`, or `undefined` if not found. By default unwraps
* scalar values from their surrounding node; to disable set `keepScalar` to
* `true` (collections are always returned intact).
*/
getIn(path2, keepScalar) {
const [key, ...rest] = path2;
const node = this.get(key, true);
if (rest.length === 0)
return !keepScalar && isScalar$1(node) ? node.value : node;
else
return isCollection$1(node) ? node.getIn(rest, keepScalar) : void 0;
}
hasAllNullValues(allowScalar) {
return this.items.every((node) => {
if (!isPair(node))
return false;
const n = node.value;
return n == null || allowScalar && isScalar$1(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag;
});
}
/**
* Checks if the collection includes a value with the key `key`.
*/
hasIn(path2) {
const [key, ...rest] = path2;
if (rest.length === 0)
return this.has(key);
const node = this.get(key, true);
return isCollection$1(node) ? node.hasIn(rest) : false;
}
/**
* Sets a value in this collection. For `!!set`, `value` needs to be a
* boolean to add/remove the item from the set.
*/
setIn(path2, value) {
const [key, ...rest] = path2;
if (rest.length === 0) {
this.set(key, value);
} else {
const node = this.get(key, true);
if (isCollection$1(node))
node.setIn(rest, value);
else if (node === void 0 && this.schema)
this.set(key, collectionFromPath(this.schema, rest, value));
else
throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
}
}
}
const stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#");
function indentComment(comment, indent) {
if (/^\n+$/.test(comment))
return comment.substring(1);
return indent ? comment.replace(/^(?! *$)/gm, indent) : comment;
}
const lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment;
const FOLD_FLOW = "flow";
const FOLD_BLOCK = "block";
const FOLD_QUOTED = "quoted";
function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
if (!lineWidth || lineWidth < 0)
return text;
if (lineWidth < minContentWidth)
minContentWidth = 0;
const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
if (text.length <= endStep)
return text;
const folds = [];
const escapedFolds = {};
let end = lineWidth - indent.length;
if (typeof indentAtStart === "number") {
if (indentAtStart > lineWidth - Math.max(2, minContentWidth))
folds.push(0);
else
end = lineWidth - indentAtStart;
}
let split = void 0;
let prev = void 0;
let overflow = false;
let i = -1;
let escStart = -1;
let escEnd = -1;
if (mode === FOLD_BLOCK) {
i = consumeMoreIndentedLines(text, i, indent.length);
if (i !== -1)
end = i + endStep;
}
for (let ch; ch = text[i += 1]; ) {
if (mode === FOLD_QUOTED && ch === "\\") {
escStart = i;
switch (text[i + 1]) {
case "x":
i += 3;
break;
case "u":
i += 5;
break;
case "U":
i += 9;
break;
default:
i += 1;
}
escEnd = i;
}
if (ch === "\n") {
if (mode === FOLD_BLOCK)
i = consumeMoreIndentedLines(text, i, indent.length);
end = i + indent.length + endStep;
split = void 0;
} else {
if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") {
const next = text[i + 1];
if (next && next !== " " && next !== "\n" && next !== " ")
split = i;
}
if (i >= end) {
if (split) {
folds.push(split);
end = split + endStep;
split = void 0;
} else if (mode === FOLD_QUOTED) {
while (prev === " " || prev === " ") {
prev = ch;
ch = text[i += 1];
overflow = true;
}
const j = i > escEnd + 1 ? i - 2 : escStart - 1;
if (escapedFolds[j])
return text;
folds.push(j);
escapedFolds[j] = true;
end = j + endStep;
split = void 0;
} else {
overflow = true;
}
}
}
prev = ch;
}
if (overflow && onOverflow)
onOverflow();
if (folds.length === 0)
return text;
if (onFold)
onFold();
let res = text.slice(0, folds[0]);
for (let i2 = 0; i2 < folds.length; ++i2) {
const fold = folds[i2];
const end2 = folds[i2 + 1] || text.length;
if (fold === 0)
res = `
${indent}${text.slice(0, end2)}`;
else {
if (mode === FOLD_QUOTED && escapedFolds[fold])
res += `${text[fold]}\\`;
res += `
${indent}${text.slice(fold + 1, end2)}`;
}
}
return res;
}
function consumeMoreIndentedLines(text, i, indent) {
let end = i;
let start2 = i + 1;
let ch = text[start2];
while (ch === " " || ch === " ") {
if (i < start2 + indent) {
ch = text[++i];
} else {
do {
ch = text[++i];
} while (ch && ch !== "\n");
end = i;
start2 = i + 1;
ch = text[start2];
}
}
return end;
}
const getFoldOptions = (ctx, isBlock2) => ({
indentAtStart: isBlock2 ? ctx.indent.length : ctx.indentAtStart,
lineWidth: ctx.options.lineWidth,
minContentWidth: ctx.options.minContentWidth
});
const containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str);
function lineLengthOverLimit(str, lineWidth, indentLength) {
if (!lineWidth || lineWidth < 0)
return false;
const limit = lineWidth - indentLength;
const strLen = str.length;
if (strLen <= limit)
return false;
for (let i = 0, start2 = 0; i < strLen; ++i) {
if (str[i] === "\n") {
if (i - start2 > limit)
return true;
start2 = i + 1;
if (strLen - start2 <= limit)
return false;
}
}
return true;
}
function doubleQuotedString(value, ctx) {
const json = JSON.stringify(value);
if (ctx.options.doubleQuotedAsJSON)
return json;
const { implicitKey } = ctx;
const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength;
const indent = ctx.indent || (containsDocumentMarker(value) ? " " : "");
let str = "";
let start2 = 0;
for (let i = 0, ch = json[i]; ch; ch = json[++i]) {
if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") {
str += json.slice(start2, i) + "\\ ";
i += 1;
start2 = i;
ch = "\\";
}
if (ch === "\\")
switch (json[i + 1]) {
case "u":
{
str += json.slice(start2, i);
const code = json.substr(i + 2, 4);
switch (code) {
case "0000":
str += "\\0";
break;
case "0007":
str += "\\a";
break;
case "000b":
str += "\\v";
break;
case "001b":
str += "\\e";
break;
case "0085":
str += "\\N";
break;
case "00a0":
str += "\\_";
break;
case "2028":
str += "\\L";
break;
case "2029":
str += "\\P";
break;
default:
if (code.substr(0, 2) === "00")
str += "\\x" + code.substr(2);
else
str += json.substr(i, 6);
}
i += 5;
start2 = i + 1;
}
break;
case "n":
if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) {
i += 1;
} else {
str += json.slice(start2, i) + "\n\n";
while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') {
str += "\n";
i += 2;
}
str += indent;
if (json[i + 2] === " ")
str += "\\";
i += 1;
start2 = i + 1;
}
break;
default:
i += 1;
}
}
str = start2 ? str + json.slice(start2) : json;
return implicitKey ? str : foldFlowLines(str, indent, FOLD_QUOTED, getFoldOptions(ctx, false));
}
function singleQuotedString(value, ctx) {
if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value))
return doubleQuotedString(value, ctx);
const indent = ctx.indent || (containsDocumentMarker(value) ? " " : "");
const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$&
${indent}`) + "'";
return ctx.implicitKey ? res : foldFlowLines(res, indent, FOLD_FLOW, getFoldOptions(ctx, false));
}
function quotedString(value, ctx) {
const { singleQuote } = ctx.options;
let qs;
if (singleQuote === false)
qs = doubleQuotedString;
else {
const hasDouble = value.includes('"');
const hasSingle = value.includes("'");
if (hasDouble && !hasSingle)
qs = singleQuotedString;
else if (hasSingle && !hasDouble)
qs = doubleQuotedString;
else
qs = singleQuote ? singleQuotedString : doubleQuotedString;
}
return qs(value, ctx);
}
let blockEndNewlines;
try {
blockEndNewlines = new RegExp("(^|(?<!\n))\n+(?!\n|$)", "g");
} catch {
blockEndNewlines = /\n+(?!\n|$)/g;
}
function blockString({ comment, type, value }, ctx, onComment, onChompKeep) {
const { blockQuote, commentString, lineWidth } = ctx.options;
if (!blockQuote || /\n[\t ]+$/.test(value) || /^\s*$/.test(value)) {
return quotedString(value, ctx);
}
const indent = ctx.indent || (ctx.forceBlockIndent || containsDocumentMarker(value) ? " " : "");
const literal = blockQuote === "literal" ? true : blockQuote === "folded" || type === Scalar.BLOCK_FOLDED ? false : type === Scalar.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, lineWidth, indent.length);
if (!value)
return literal ? "|\n" : ">\n";
let chomp;
let endStart;
for (endStart = value.length; endStart > 0; --endStart) {
const ch = value[endStart - 1];
if (ch !== "\n" && ch !== " " && ch !== " ")
break;
}
let end = value.substring(endStart);
const endNlPos = end.indexOf("\n");
if (endNlPos === -1) {
chomp = "-";
} else if (value === end || endNlPos !== end.length - 1) {
chomp = "+";
if (onChompKeep)
onChompKeep();
} else {
chomp = "";
}
if (end) {
value = value.slice(0, -end.length);
if (end[end.length - 1] === "\n")
end = end.slice(0, -1);
end = end.replace(blockEndNewlines, `$&${indent}`);
}
let startWithSpace = false;
let startEnd;
let startNlPos = -1;
for (startEnd = 0; startEnd < value.length; ++startEnd) {
const ch = value[startEnd];
if (ch === " ")
startWithSpace = true;
else if (ch === "\n")
startNlPos = startEnd;
else
break;
}
let start2 = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd);
if (start2) {
value = value.substring(start2.length);
start2 = start2.replace(/\n+/g, `$&${indent}`);
}
const indentSize = indent ? "2" : "1";
let header = (startWithSpace ? indentSize : "") + chomp;
if (comment) {
header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " "));
if (onComment)
onComment();
}
if (!literal) {
const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`);
let literalFallback = false;
const foldOptions = getFoldOptions(ctx, true);
if (blockQuote !== "folded" && type !== Scalar.BLOCK_FOLDED) {
foldOptions.onOverflow = () => {
literalFallback = true;
};
}
const body = foldFlowLines(`${start2}${foldedValue}${end}`, indent, FOLD_BLOCK, foldOptions);
if (!literalFallback)
return `>${header}
${indent}${body}`;
}
value = value.replace(/\n+/g, `$&${indent}`);
return `|${header}
${indent}${start2}${value}${end}`;
}
function plainString(item, ctx, onComment, onChompKeep) {
const { type, value } = item;
const { actualString, implicitKey, indent, indentStep, inFlow } = ctx;
if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) {
return quotedString(value, ctx);
}
if (!value || /^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) {
return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep);
}
if (!implicitKey && !inFlow && type !== Scalar.PLAIN && value.includes("\n")) {
return blockString(item, ctx, onComment, onChompKeep);
}
if (containsDocumentMarker(value)) {
if (indent === "") {
ctx.forceBlockIndent = true;
return blockString(item, ctx, onComment, onChompKeep);
} else if (implicitKey && indent === indentStep) {
return quotedString(value, ctx);
}
}
const str = value.replace(/\n+/g, `$&
${indent}`);
if (actualString) {
const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str);
const { compat, tags } = ctx.doc.schema;
if (tags.some(test) || compat?.some(test))
return quotedString(value, ctx);
}
return implicitKey ? str : foldFlowLines(str, indent, FOLD_FLOW, getFoldOptions(ctx, false));
}
function stringifyString(item, ctx, onComment, onChompKeep) {
const { implicitKey, inFlow } = ctx;
const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) });
let { type } = item;
if (type !== Scalar.QUOTE_DOUBLE) {
if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value))
type = Scalar.QUOTE_DOUBLE;
}
const _stringify = (_type) => {
switch (_type) {
case Scalar.BLOCK_FOLDED:
case Scalar.BLOCK_LITERAL:
return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep);
case Scalar.QUOTE_DOUBLE:
return doubleQuotedString(ss.value, ctx);
case Scalar.QUOTE_SINGLE:
return singleQuotedString(ss.value, ctx);
case Scalar.PLAIN:
return plainString(ss, ctx, onComment, onChompKeep);
default:
return null;
}
};
let res = _stringify(type);
if (res === null) {
const { defaultKeyType, defaultStringType } = ctx.options;
const t = implicitKey && defaultKeyType || defaultStringType;
res = _stringify(t);
if (res === null)
throw new Error(`Unsupported default string type ${t}`);
}
return res;
}
function createStringifyContext(doc, options) {
const opt = Object.assign({
blockQuote: true,
commentString: stringifyComment,
defaultKeyType: null,
defaultStringType: "PLAIN",
directives: null,
doubleQuotedAsJSON: false,
doubleQuotedMinMultiLineLength: 40,
falseStr: "false",
flowCollectionPadding: true,
indentSeq: true,
lineWidth: 80,
minContentWidth: 20,
nullStr: "null",
simpleKeys: false,
singleQuote: null,
trueStr: "true",
verifyAliasOrder: true
}, doc.schema.toStringOptions, options);
let inFlow;
switch (opt.collectionStyle) {
case "block":
inFlow = false;
break;
case "flow":
inFlow = true;
break;
default:
inFlow = null;
}
return {
anchors: /* @__PURE__ */ new Set(),
doc,
flowCollectionPadding: opt.flowCollectionPadding ? " " : "",
indent: "",
indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ",
inFlow,
options: opt
};
}
function getTagObject(tags, item) {
if (item.tag) {
const match = tags.filter((t) => t.tag === item.tag);
if (match.length > 0)
return match.find((t) => t.format === item.format) ?? match[0];
}
let tagObj = void 0;
let obj;
if (isScalar$1(item)) {
obj = item.value;
let match = tags.filter((t) => t.identify?.(obj));
if (match.length > 1) {
const testMatch = match.filter((t) => t.test);
if (testMatch.length > 0)
match = testMatch;
}
tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format);
} else {
obj = item;
tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass);
}
if (!tagObj) {
const name = obj?.constructor?.name ?? typeof obj;
throw new Error(`Tag not resolved for ${name} value`);
}
return tagObj;
}
function stringifyProps(node, tagObj, { anchors, doc }) {
if (!doc.directives)
return "";
const props = [];
const anchor = (isScalar$1(node) || isCollection$1(node)) && node.anchor;
if (anchor && anchorIsValid(anchor)) {
anchors.add(anchor);
props.push(`&${anchor}`);
}
const tag = node.tag ? node.tag : tagObj.default ? null : tagObj.tag;
if (tag)
props.push(doc.directives.tagString(tag));
return props.join(" ");
}
function stringify$2(item, ctx, onComment, onChompKeep) {
if (isPair(item))
return item.toString(ctx, onComment, onChompKeep);
if (isAlias(item)) {
if (ctx.doc.directives)
return item.toString(ctx);
if (