@bufbuild/cel
Version:
A CEL evaluator for ECMAScript
157 lines (156 loc) • 4.24 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.
import { isCelList } from "./list.js";
import { isCelMap } from "./map.js";
import { isCelUint } from "./uint.js";
import { isReflectMessage, } from "@bufbuild/protobuf/reflect";
import { TimestampSchema, DurationSchema } from "@bufbuild/protobuf/wkt";
const privateSymbol = Symbol.for("@bufbuild/cel/type");
/**
* Scalar CEL value types.
*/
// biome-ignore format: indented for readability
export const CelScalar = {
INT: celScalarType("int"),
UINT: celScalarType("uint"),
BOOL: celScalarType("bool"),
STRING: celScalarType("string"),
BYTES: celScalarType("bytes"),
DOUBLE: celScalarType("double"),
NULL: celScalarType("null_type"),
DYN: celScalarType("dyn"),
TYPE: celScalarType("type"),
};
/**
* Represents the CEL type google.protobuf.Timestamp.
*/
export const TIMESTAMP = objectType(TimestampSchema);
/**
* Represents the CEL type google.protobuf.Duration.
*/
export const DURATION = objectType(DurationSchema);
/**
* Creates a new CelMapType.
*/
export function mapType(key, value) {
return {
[privateSymbol]: {},
kind: "map",
key,
value,
name: "map",
toString() {
return `map(${key}, ${value})`;
},
};
}
/**
* Creates a new CelListType.
*/
export function listType(element) {
return {
[privateSymbol]: {},
kind: "list",
element,
name: "list",
toString() {
return `list(${element})`;
},
};
}
/**
* Creates a new CelTypeType
*/
export function typeType(type) {
return {
[privateSymbol]: {},
kind: "type",
type,
name: "type",
toString() {
return "type";
},
};
}
/**
* Creates a new CelObjectType.
*/
export function objectType(desc) {
return {
[privateSymbol]: {},
kind: "object",
desc,
name: desc.typeName,
toString() {
return desc.name;
},
};
}
function celScalarType(scalar) {
return {
[privateSymbol]: {},
kind: "scalar",
scalar,
name: scalar,
toString() {
return scalar;
},
};
}
/**
* Get the CelType of a CelValue.
*/
export function celType(v) {
switch (typeof v) {
case "bigint":
return CelScalar.INT;
case "boolean":
return CelScalar.BOOL;
case "number":
return CelScalar.DOUBLE;
case "string":
return CelScalar.STRING;
case "object":
switch (true) {
case v === null:
return CelScalar.NULL;
case v instanceof Uint8Array:
return CelScalar.BYTES;
case isReflectMessage(v):
return objectType(v.desc);
case isCelList(v):
return listType(CelScalar.DYN);
case isCelMap(v):
return mapType(CelScalar.DYN, CelScalar.DYN);
case isCelUint(v):
return CelScalar.UINT;
default:
// This can also be a case statement, but TS fails to
// narrow the type.
if (isObjectCelType(v)) {
return typeType(v);
}
}
}
throw new Error(`Not a CEL value: ${v}`);
}
/**
* Returns true if the given value is a CelType.
*/
export function isCelType(v) {
return typeof v === "object" && v !== null && isObjectCelType(v);
}
export function isObjectCelType(v) {
return privateSymbol in v;
}