openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
169 lines (168 loc) • 7.04 kB
JavaScript
import { t as isBlockedObjectKey } from "./prototype-keys-CuYw53fZ.js";
import { t as parseConfigPathArrayIndex } from "./path-array-index-CvEcUJa-.js";
//#region src/shared/dot-path.ts
function parseQuotedBracketPathSegment(value) {
const quote = value.startsWith("\"") ? "\"" : "'";
if (quote === "\"") try {
return JSON.parse(value);
} catch {}
if (!value.endsWith(quote)) throw new SyntaxError("Unterminated quoted path segment");
let normalized = "\"";
for (let index = 1; index < value.length - 1; index += 1) {
const character = value[index];
if (character === quote) throw new SyntaxError("Unexpected quote in path segment");
if (character === "\"") {
normalized += "\\\"";
continue;
}
if (character === "\\") {
const escaped = value[index + 1];
if (escaped === void 0 || index + 1 >= value.length - 1) throw new SyntaxError("Unterminated escape in path segment");
index += 1;
if (escaped === "\n" || escaped === "\u2028" || escaped === "\u2029") continue;
if (escaped === "\r") {
if (value[index + 1] === "\n") index += 1;
continue;
}
if (escaped === "x") {
const hex = value.slice(index + 1, index + 3);
if (!/^[\da-f]{2}$/i.test(hex)) throw new SyntaxError("Invalid hexadecimal escape in path segment");
normalized += `\\u00${hex}`;
index += 2;
continue;
}
if (escaped === "0") {
if (/\d/.test(value[index + 1] ?? "")) throw new SyntaxError("Invalid numeric escape in path segment");
normalized += "\\u0000";
continue;
}
if (escaped === "v") {
normalized += "\\u000b";
continue;
}
if (/[1-9]/.test(escaped)) throw new SyntaxError("Invalid numeric escape in path segment");
if (escaped === "'") normalized += "'";
else if ("\"\\/bfnrtu".includes(escaped)) normalized += `\\${escaped}`;
else normalized += escaped;
continue;
}
normalized += character;
}
return JSON.parse(`${normalized}"`);
}
function parseBracketPathSegment(raw, fullPath) {
const trimmed = raw.trim();
if (!trimmed) throw new Error(`Invalid path (empty "[]"): ${fullPath}`);
if (trimmed.startsWith("\"") || trimmed.startsWith("'")) {
try {
const parsed = parseQuotedBracketPathSegment(trimmed);
if (typeof parsed === "string" && parsed.trim()) return parsed;
} catch (err) {
throw new Error(`Invalid path bracket string (${trimmed}): ${fullPath}`, { cause: err });
}
throw new Error(`Invalid path bracket string (${trimmed}): ${fullPath}`);
}
return parseConfigPathArrayIndex(trimmed) ?? trimmed;
}
function assertNotWhitespaceSegment(current, raw) {
if (current.length > 0 && !current.trim()) throw new Error(`Invalid path (empty segment): ${raw}`);
}
function findBracketPathClose(path, open) {
let quote;
for (let index = open + 1; index < path.length; index += 1) {
const character = path[index];
if (quote) {
if (character === "\\") index += 1;
else if (character === quote) quote = void 0;
continue;
}
if (character === "]") return index;
if ((character === "\"" || character === "'") && !path.slice(open + 1, index).trim()) quote = character;
}
return -1;
}
/** Retains quoted numeric-key provenance alongside the public concrete path tokens. */
function parseConcreteConfigPathWithProvenance(raw) {
const trimmed = raw.trim();
if (!trimmed) throw new Error("Path is empty.");
const parts = [];
const quotedNumericSegments = /* @__PURE__ */ new Set();
let current = "";
let segmentEmitted = false;
let index = 0;
while (index < trimmed.length) {
const character = trimmed[index];
if (character === "\\") {
const next = trimmed[index + 1];
if (next === void 0) throw new Error(`Invalid path (trailing escape): ${raw}`);
current += next;
index += 2;
continue;
}
if (character === ".") {
assertNotWhitespaceSegment(current, raw);
if (!segmentEmitted && !current.trim()) throw new Error(`Invalid path (empty segment): ${raw}`);
if (current) parts.push(current.trim());
current = "";
segmentEmitted = false;
index += 1;
continue;
}
if (character === "[") {
assertNotWhitespaceSegment(current, raw);
if (!current.trim() && !segmentEmitted && parts.length > 0) throw new Error(`Invalid path (empty segment): ${raw}`);
if (current) parts.push(current.trim());
current = "";
const close = findBracketPathClose(trimmed, index);
if (close === -1) throw new Error(`Invalid path (missing "]"): ${raw}`);
const inside = trimmed.slice(index + 1, close).trim();
if (!inside) throw new Error(`Invalid path (empty "[]"): ${raw}`);
const segment = parseBracketPathSegment(inside, raw);
if (typeof segment === "string" && (inside.startsWith("\"") || inside.startsWith("'")) && parseConfigPathArrayIndex(segment) !== void 0) quotedNumericSegments.add(parts.length);
parts.push(segment);
const next = trimmed[close + 1];
if (next !== void 0 && next !== "." && next !== "[") throw new Error(`Invalid path (missing separator after bracket): ${raw}`);
segmentEmitted = true;
index = close + 1;
continue;
}
current += character;
index += 1;
}
if (!segmentEmitted && !current.trim()) throw new Error(`Invalid path (empty segment): ${raw}`);
if (current) parts.push(current.trim());
for (const segment of parts) if (typeof segment === "string" && isBlockedObjectKey(segment)) throw new Error(`Invalid path segment: ${segment}`);
return {
tokens: parts,
quotedNumericSegments
};
}
/** Parses one concrete path while keeping explicit array brackets distinct from quoted keys. */
function parseConcreteConfigPathTokens(raw) {
return parseConcreteConfigPathWithProvenance(raw).tokens;
}
/** Parses one concrete config path into the existing string-segment CLI contract. */
function parseConcreteConfigPath(raw) {
return parseConcreteConfigPathTokens(raw).map(String);
}
/** Appends one config path segment without confusing literal record keys with traversal. */
function appendConfigPathSegment(path, segment) {
if (typeof segment === "number") return `${path}[${segment}]`;
if (!/^[A-Za-z_$][A-Za-z0-9_$:-]*$/.test(segment)) return `${path}[${JSON.stringify(segment)}]`;
return path ? `${path}.${segment}` : segment;
}
/** Formats concrete tokens, recovering array indices from their actual source containers. */
function formatConcreteConfigPath(segments, source) {
let cursor = source;
return segments.reduce((path, segment) => {
const concreteSegment = typeof segment === "string" && Array.isArray(cursor) ? parseConfigPathArrayIndex(segment) ?? segment : segment;
cursor = cursor !== null && typeof cursor === "object" ? Reflect.get(cursor, String(segment)) : void 0;
return appendConfigPathSegment(path, concreteSegment);
}, "");
}
/** Joins path segments into their dotted-path representation. */
function toDotPath(segments) {
return segments.join(".");
}
//#endregion
export { parseConcreteConfigPathWithProvenance as a, parseConcreteConfigPathTokens as i, formatConcreteConfigPath as n, toDotPath as o, parseConcreteConfigPath as r, appendConfigPathSegment as t };