@bufbuild/cel
Version:
A CEL evaluator for ECMAScript
161 lines (160 loc) • 5.02 kB
JavaScript
;
// 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.
Object.defineProperty(exports, "__esModule", { value: true });
exports.toCel = toCel;
exports.unwrapAny = unwrapAny;
exports.reflectMsgToCel = reflectMsgToCel;
const uint_js_1 = require("./uint.js");
const map_js_1 = require("./map.js");
const list_js_1 = require("./list.js");
const reflect_1 = require("@bufbuild/protobuf/reflect");
const protobuf_1 = require("@bufbuild/protobuf");
const eval_js_1 = require("./eval.js");
const wkt_1 = require("@bufbuild/protobuf/wkt");
const type_js_1 = require("./type.js");
/**
* Converts a CelInput to a CelValue.
*/
function toCel(v) {
switch (typeof v) {
case "bigint":
case "boolean":
case "number":
case "string":
return v;
case "object":
break;
default:
throw new Error(`unsupported input ${typeof v}`);
}
switch (true) {
case v === null:
case v instanceof Uint8Array:
case (0, list_js_1.isCelList)(v):
case (0, map_js_1.isCelMap)(v):
case (0, uint_js_1.isCelUint)(v):
case (0, type_js_1.isCelType)(v):
return v;
}
if (isArray(v) || (0, reflect_1.isReflectList)(v)) {
return (0, list_js_1.celList)(v);
}
if (isMap(v) || (0, reflect_1.isReflectMap)(v)) {
return (0, map_js_1.celMap)(v);
}
if ((0, protobuf_1.isMessage)(v)) {
const value = wktToCel(v);
if (value !== undefined) {
return value;
}
return (0, reflect_1.reflect)((0, eval_js_1.getMsgDesc)(v.$typeName), v);
}
if ((0, reflect_1.isReflectMessage)(v)) {
return reflectMsgToCel(v);
}
if (v.constructor.name === "Object") {
return (0, map_js_1.celMap)(new Map(Object.entries(v)));
}
throw new Error(`Unsupported input ${v}`);
}
/**
* Unwraps the given value if it is an Any.
*
* If the Any represents a Wrapper type or google.protobuf.Value/Struct/ListValue, it also converts them
* to their corresponding CelValue.
*/
function unwrapAny(v) {
if (!isReflectAny(v)) {
return v;
}
const unpacked = (0, wkt_1.anyUnpack)(v.message, (0, eval_js_1.getEvalContext)().registry);
if (unpacked === undefined) {
throw new Error(`invalid Any or ${v.message.typeUrl} not found in registry`);
}
const value = wktToCel(unpacked);
if (value !== undefined) {
return value;
}
return (0, reflect_1.reflect)((0, eval_js_1.getMsgDesc)(unpacked.$typeName), unpacked);
}
function reflectMsgToCel(v) {
const value = wktToCel(v.message);
if (value !== undefined) {
return value;
}
return v;
}
function isReflectAny(v) {
return (0, reflect_1.isReflectMessage)(v, wkt_1.AnySchema);
}
function isArray(v) {
return Array.isArray(v);
}
function isMap(v) {
return v instanceof Map;
}
function wktToCel(msg) {
if ((0, wkt_1.isWrapper)(msg)) {
return unwrapWrapper(msg);
}
return jsonWrapperToCel(msg);
}
function unwrapWrapper(v) {
switch (v.$typeName) {
case wkt_1.Int32ValueSchema.typeName:
return BigInt(v.value);
case wkt_1.UInt32ValueSchema.typeName:
return (0, uint_js_1.celUint)(BigInt(v.value));
case wkt_1.UInt64ValueSchema.typeName:
return (0, uint_js_1.celUint)(v.value);
}
return v.value;
}
function jsonWrapperToCel(msg) {
switch (true) {
case (0, protobuf_1.isMessage)(msg, wkt_1.StructSchema):
return structToCel(msg);
case (0, protobuf_1.isMessage)(msg, wkt_1.ValueSchema):
return valueToCel(msg);
case (0, protobuf_1.isMessage)(msg, wkt_1.ListValueSchema):
return listValueToCel(msg);
}
return undefined;
}
function structToCel(s) {
const map = new Map();
for (const [k, v] of Object.entries(s.fields)) {
map.set(k, valueToCel(v));
}
return (0, map_js_1.celMap)(map);
}
function valueToCel(v) {
switch (v.kind.case) {
case "boolValue":
case "numberValue":
case "stringValue":
return v.kind.value;
case "nullValue":
case undefined:
return null;
case "structValue":
return structToCel(v.kind.value);
case "listValue":
return listValueToCel(v.kind.value);
}
}
function listValueToCel(l) {
return (0, list_js_1.celList)(l.values);
}