@bizone-ai/json-transform-utils
Version:
Utilities for handling JSON transformers
233 lines • 10 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSubfunction = exports.functionsParser = exports.FunctionsParser = exports.getFunctionObjectSignature = exports.getFunctionInlineSignature = void 0;
const parseDefinitions_1 = __importDefault(require("./parseDefinitions"));
const functions_1 = __importDefault(require("./functions"));
const json_transform_1 = require("@bizone-ai/json-transform");
const JSONStringEndFinder = /(?:[^"\\]|\\.)*"/;
function compareArgumentPosition(a, b) {
return (a.position ?? Infinity) - (b.position ?? Infinity);
}
const getFunctionInlineSignature = (name, func, requiredOnly) => {
return ("$$" +
name +
(func.arguments?.some(a => a.position === 0 && a.required)
? "(" +
func.arguments
.filter(a => typeof a.position === "number" && (!requiredOnly || a.required))
.sort(compareArgumentPosition)
.map(a => (a.type === "const" ? a.const : a.required ? "{" + a.name + "}" : "[" + a.name + "]"))
.join(",") +
")"
: ""));
};
exports.getFunctionInlineSignature = getFunctionInlineSignature;
const getFunctionObjectSignature = (name, func) => {
return `{ "\\$\\$${name}": {input} ${func.arguments?.some(a => a.position === 0 && a.required)
? "," +
func.arguments
.filter(a => typeof a.position === "number" && a.required)
.sort(compareArgumentPosition)
.map(a => `"${a.name}": {${a.name}}`)
.join(", ")
: ""}}`;
};
exports.getFunctionObjectSignature = getFunctionObjectSignature;
class FunctionsParser {
constructor() {
this.clientFunctions = {};
const functionNames = new Set(Object.keys(functions_1.default));
// add aliases
for (const name of functionNames) {
if (functions_1.default[name].aliases) {
functions_1.default[name].aliases?.forEach(alias => functionNames.add(alias));
}
}
this.allFunctionsNames = functionNames;
}
setClientFunctions(clientFunctions, handler, docsUrlResolver) {
this.clientFunctions = (0, parseDefinitions_1.default)(clientFunctions, true);
this.clientDocsUrlResolver = docsUrlResolver;
for (const name in clientFunctions) {
this.allFunctionsNames.add(name);
if (clientFunctions[name].aliases) {
clientFunctions[name].aliases?.forEach(alias => this.allFunctionsNames.add(alias));
}
}
this.handleClientFunction = handler;
}
get(name, args) {
return this.clientFunctions?.[name] ?? functions_1.default[name];
}
getNames() {
return this.allFunctionsNames;
}
resolveDocsUrl(funcName, functionDescriptor) {
const name = functionDescriptor?.aliasTo ?? funcName;
if (functionDescriptor?.custom && this.clientDocsUrlResolver) {
const url = this.clientDocsUrlResolver(name);
if (url) {
return url;
}
}
return `https://bizone-ai.github.io/json-transform/functions/${name}`;
}
matchInline(data, callback) {
if (typeof data !== "string")
return null;
const m = json_transform_1.InlineFunctionTokenizer.tokenize(data);
if (!m) {
return null;
}
let funcName = m.name;
let func = exports.functionsParser.get(funcName);
if (!func)
return null;
if (func.aliasTo)
funcName = func.aliasTo;
if (func.subfunctions || callback) {
const args = m.args?.reduce((a, c, i) => {
func.arguments?.forEach(fa => {
if (fa.position === i && fa.name) {
a[fa.name] = c.value;
}
});
return a;
}, {}) ?? {};
func = (0, exports.getSubfunction)(func, args);
if (callback) {
const inlineFunctionValue = m.input?.value;
callback(funcName, func, inlineFunctionValue, args);
}
}
return func;
}
matchObject(data, extractOutputTypeRecursively) {
if (!data || typeof data !== "object")
return;
for (const funcName of this.allFunctionsNames) {
const key = "$$" + funcName;
const value = data[key];
if (typeof value !== "undefined") {
if (extractOutputTypeRecursively && this.get(funcName).pipedType) {
const match = this.matchObject(value, true);
if (match)
return match;
}
const func = (0, exports.getSubfunction)(this.get(funcName), data);
const args = { ...data };
delete args[key]; // remove the function key from args
if (func.argumentsAsInputSchema && Object.keys(args).length === 0 && Array.isArray(value)) {
value.forEach((argVal, position) => {
const arg = func.arguments?.find(a => a.position === position);
if (arg) {
args[arg.name] = argVal;
}
});
}
return {
name: func.aliasTo ?? funcName,
func,
value,
args,
};
}
}
}
matchAllFunctionsInLine(line) {
const matches = [];
const inputLine = line.trimEnd();
let inputLineOffset = 0;
while (inputLineOffset < line.length) {
let line = inputLine.substring(inputLineOffset);
let indexOffset = line.indexOf("$$");
if (indexOffset === -1) {
break; // no more functions in line
}
if (line[indexOffset - 1] === '"') {
// function might be quoted, find next indexOf " (and then cut the quotes and escape apostrophes)
const jsonStringEnd = JSONStringEndFinder.exec(line.slice(indexOffset));
if (jsonStringEnd?.[0]) {
line = line
.slice(0, indexOffset + jsonStringEnd[0].length - 1)
//
.replace(/\\'/g, "\\\\'");
//.replace(/(\\)?'/g, "$1\\'");
}
}
else {
inputLineOffset += indexOffset + 2; // skip this $$ and find the next one
break;
}
let match;
let str = line.substring(indexOffset);
let newInputOffset = -1;
while ((match = json_transform_1.InlineFunctionTokenizer.tokenize(str))) {
if (!exports.functionsParser.get(match.name)) {
// not a known function name (so not a function), skip it on next iteration
newInputOffset = inputLineOffset + match.keyLength;
break;
}
match.index = inputLineOffset + indexOffset; // add index to match
match.args?.forEach(arg => {
arg.index += inputLineOffset + indexOffset;
// arg.value = arg.value? ?? null;
const delta = arg.value?.match(/(?<=\\)'/g)?.length ?? 0;
arg.length -= delta;
indexOffset -= delta;
});
if (match.input) {
match.input.index += inputLineOffset + indexOffset;
// match.input.value = match.input.value?.replace(/\\'/g, "'") ?? null;
const delta = match.input.value?.match(/(?<=\\)'/g)?.length ?? 0;
match.input.length -= delta;
indexOffset -= delta;
}
if (matches.length) {
const last = matches[matches.length - 1];
if (last.index === match.index) {
console.warn(`Detected a parsing loop, stopping at (${last.index})`);
newInputOffset = inputLine.length;
break;
}
}
matches.push(match);
if (newInputOffset < 0) {
newInputOffset = match.input
? match.input.index + match.input.length
: match.args
? match.args.reduce((a, c) => a + c.index + c.length + 1, -1)
: match.index + match.keyLength;
}
if (!match.input || !match.input.value?.startsWith("$$")) {
break;
}
indexOffset = match.input.index - inputLineOffset;
str = line.substring(indexOffset);
}
if (newInputOffset < 0) {
break; // did not match in this iteration
}
inputLineOffset = newInputOffset;
newInputOffset = -1;
}
return matches;
}
}
exports.FunctionsParser = FunctionsParser;
exports.functionsParser = new FunctionsParser();
const getSubfunction = (func, args) => {
if (!func.subfunctions || !args)
return func;
for (const subFunc of func.subfunctions) {
if (subFunc.if.every(c => (args[c.argument] ?? subFunc.then.defaultValues?.[c.argument])?.toString().toUpperCase() === c.equals)) {
return subFunc.then; // replace func instance based on args
}
}
return func;
};
exports.getSubfunction = getSubfunction;
//# sourceMappingURL=functionsParser.js.map
;