@bufbuild/cel
Version:
A CEL evaluator for ECMAScript
207 lines (206 loc) • 6.28 kB
JavaScript
"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;
}