UNPKG

@bufbuild/cel

Version:

A CEL evaluator for ECMAScript

66 lines (65 loc) 2.56 kB
// 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. import { CheckedExprSchema, } from "@bufbuild/cel-spec/cel/expr/checked_pb.js"; import { ExprSchema, ParsedExprSchema, } from "@bufbuild/cel-spec/cel/expr/syntax_pb.js"; import { Planner } from "./planner.js"; import { create, isMessage } from "@bufbuild/protobuf"; import { celError, isCelError } from "./error.js"; import { withEvalContext } from "./eval.js"; import { unwrapAny } from "./value.js"; import { EMPTY_ACTIVATION, ObjectActivation } from "./activation.js"; const cache = new WeakMap(); /** * Creates an execution plan for a CEL expression and returns a reusable evaluation function. * * Planning analyzes the expression structure once, independent of runtime variable values. * The returned function can be called multiple times with different variable bindings. */ export function plan(env, expr) { // TODO(srikrsna): This can be avoided, if we refactor Planner into functions that use CelEnv directly. let planner = cache.get(env); if (planner === undefined) { planner = new Planner(env.dispatcher, env.registry, env.namespace); cache.set(env, planner); } let maybeExpr; if (isMessage(expr, CheckedExprSchema)) { maybeExpr = expr.expr; } else if (isMessage(expr, ParsedExprSchema)) { maybeExpr = expr.expr; } else { maybeExpr = expr; } const plan = planner.plan(maybeExpr ?? create(ExprSchema)); const withContext = withEvalContext({ registry: env.registry }, { id: plan.id, eval(ctx) { try { let val = plan.eval(ctx); if (isCelError(val)) { return val; } return unwrapAny(val); } catch (ex) { return celError(ex, plan.id); } }, }); return (ctx) => { return withContext.eval(ctx !== undefined ? new ObjectActivation(ctx) : EMPTY_ACTIVATION); }; }