UNPKG

@bufbuild/cel

Version:

A CEL evaluator for ECMAScript

207 lines (206 loc) 6.28 kB
"use strict"; // Copyright 2024-2025 Buf Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. var _a, _b; Object.defineProperty(exports, "__esModule", { value: true }); exports.OrderedDispatcher = exports.FuncRegistry = void 0; exports.celFunc = celFunc; exports.celOverload = celOverload; const list_js_1 = require("./list.js"); const type_js_1 = require("./type.js"); const error_js_1 = require("./error.js"); const map_js_1 = require("./map.js"); const uint_js_1 = require("./uint.js"); const reflect_1 = require("@bufbuild/protobuf/reflect"); const value_js_1 = require("./value.js"); const privateFuncSymbol = Symbol.for("@bufbuild/cel/func"); const privateOverloadSymbol = Symbol.for("@bufbuild/cel/overload"); /** * Creates a new CelFunc. */ function celFunc(name, overloads) { return new Func(name, overloads); } /** * Creates a new CelOverload. */ function celOverload(parameters, result, impl) { return new FuncOverload(parameters, result, impl); } class Func { constructor(_name, _overloads) { this._name = _name; this._overloads = _overloads; this[_a] = {}; } get name() { return this._name; } get overloads() { return this._overloads; } dispatch(id, args) { const vals = unwrapResults(args); if ((0, error_js_1.isCelError)(vals)) { return vals; } for (const overload of this._overloads) { if (overload.parameters.length !== vals.length) { continue; } const checkedVals = []; for (let i = 0; i < vals.length; i++) { const celValue = (0, value_js_1.unwrapAny)(vals[i]); if (!isOfType(celValue, overload.parameters[i])) { break; } checkedVals.push(celValue); } if (checkedVals.length !== vals.length) { continue; } try { return (0, value_js_1.toCel)(overload.impl(...checkedVals)); } catch (ex) { return (0, error_js_1.celError)(ex, id); } } return undefined; } } _a = privateFuncSymbol; class FuncOverload { constructor(_parameters, _result, _impl) { this._parameters = _parameters; this._result = _result; this._impl = _impl; this[_b] = {}; } get parameters() { return this._parameters; } get result() { return this._result; } get impl() { return this._impl; } } _b = privateOverloadSymbol; /** * Set of functions uniquely identified by their name. */ class FuncRegistry { constructor(funcs) { this.functions = new Map(); funcs && this.add(funcs); } add(nameOrFunc, call) { if (typeof nameOrFunc !== "string") { if (Array.isArray(nameOrFunc)) { for (const func of nameOrFunc) { this.add(func); } return; } call = nameOrFunc; nameOrFunc = nameOrFunc.name; } if (call === undefined) { throw new Error("dispatch is required with name"); } this.addCall(nameOrFunc, call); } /** * Find a function by name. */ find(name) { return this.functions.get(name); } addCall(name, call) { if (this.functions.has(name)) { throw new Error(`Function ${name} already registered`); } this.functions.set(name, call); } } exports.FuncRegistry = FuncRegistry; class OrderedDispatcher { constructor(dispatchers) { this.dispatchers = dispatchers; } add(dispatcher) { this.dispatchers.unshift(dispatcher); } find(name) { for (const dispatcher of this.dispatchers) { const result = dispatcher.find(name); if (result !== undefined) { return result; } } return undefined; } } exports.OrderedDispatcher = OrderedDispatcher; function isOfType(val, type) { switch (type.kind) { case "list": return (0, list_js_1.isCelList)(val); case "map": return (0, map_js_1.isCelMap)(val); case "object": return (0, reflect_1.isReflectMessage)(val, type.desc); case "type": return (0, type_js_1.isCelType)(val); case "scalar": switch (type) { case type_js_1.CelScalar.DYN: return true; case type_js_1.CelScalar.INT: return typeof val === "bigint"; case type_js_1.CelScalar.UINT: return (0, uint_js_1.isCelUint)(val); case type_js_1.CelScalar.BOOL: return typeof val === "boolean"; case type_js_1.CelScalar.DOUBLE: return typeof val === "number"; case type_js_1.CelScalar.NULL: return val === null; case type_js_1.CelScalar.STRING: return typeof val === "string"; case type_js_1.CelScalar.BYTES: return val instanceof Uint8Array; } } return false; } function unwrapResults(args) { const errors = []; const vals = []; for (let i = 0; i < args.length; i++) { const arg = args[i]; if ((0, error_js_1.isCelError)(arg)) { errors.push(arg); } else { vals.push(arg); } } if (errors.length > 0) { return (0, error_js_1.celErrorMerge)(errors[0], ...errors.slice(1)); } return vals; }