@bufbuild/cel
Version:
A CEL evaluator for ECMAScript
193 lines (192 loc) • 6.22 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.EMPTY_MAP = void 0;
exports.celMap = celMap;
exports.isCelMap = isCelMap;
const reflect_1 = require("@bufbuild/protobuf/reflect");
const uint_js_1 = require("./uint.js");
const protobuf_1 = require("@bufbuild/protobuf");
const proto_js_1 = require("./proto.js");
const value_js_1 = require("./value.js");
const privateSymbol = Symbol.for("@bufbuild/cel/map");
/**
* Create a new map from a native map or a ReflectMap.
*/
function celMap(mapOrReflectMap) {
if ((0, reflect_1.isReflectMap)(mapOrReflectMap)) {
return new ProtoMap(mapOrReflectMap);
}
return new NativeMap(mapOrReflectMap);
}
/**
* Returns true if the given value is a CelMap.
*/
function isCelMap(v) {
return typeof v === "object" && v !== null && privateSymbol in v;
}
class NativeMap {
constructor(_map) {
this._map = _map;
this[_a] = {};
}
get size() {
return this._map.size;
}
get(key) {
if ((0, uint_js_1.isCelUint)(key)) {
key = key.value;
}
// According to CEL equality all numerical types are
// equal if they have the same value.
if (typeof key === "number") {
if (!Number.isInteger(key)) {
return undefined;
}
key = BigInt(key);
}
// Direct check for maps with string, boolean, and bigint keys.
const value = this._map.get(key);
if (value !== undefined) {
return (0, value_js_1.toCel)(value);
}
// For maps with CelUint keys we have to loop through all keys to check because
// JS maps use SameValueZero algorithm which is the same as '===' for objects.
//
// Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#key_equality
if (typeof key === "bigint") {
for (const mapKey of this._map.keys()) {
if (!(0, uint_js_1.isCelUint)(mapKey)) {
continue;
}
if (mapKey.value === key) {
return (0, value_js_1.toCel)(this._map.get(mapKey));
}
}
}
return undefined;
}
has(key) {
return this.get(key) != undefined;
}
forEach(callback,
// biome-ignore lint/suspicious/noExplicitAny: Part of the Map interface.
thisArg) {
this._map.forEach((value, key, _) => callback.call(thisArg, (0, value_js_1.toCel)(value), key, this));
}
*entries() {
for (const [key, value] of this._map.entries()) {
yield [key, (0, value_js_1.toCel)(value)];
}
}
keys() {
return this._map.keys();
}
*values() {
for (const value of this._map.values()) {
yield (0, value_js_1.toCel)(value);
}
}
[(_a = privateSymbol, Symbol.iterator)]() {
return this.entries();
}
}
class ProtoMap {
constructor(_map) {
this._map = _map;
this[_b] = {};
}
get size() {
return this._map.size;
}
get(key) {
const value = this._map.get(mapKeyFromCel(this._map.field(), key));
if (value === undefined) {
return undefined;
}
return celFromMapValue(this._map.field(), value);
}
has(key) {
return this._map.has(mapKeyFromCel(this._map.field(), key));
}
forEach(callback,
// biome-ignore lint/suspicious/noExplicitAny: Part of the Map interface.
thisArg) {
this._map.forEach((value, key, _) => callback.call(thisArg, celFromMapValue(this._map.field(), value), celFromMapKey(this._map.field(), key), this));
}
*entries() {
for (const [key, value] of this._map.entries()) {
yield [
celFromMapKey(this._map.field(), key),
celFromMapValue(this._map.field(), value),
];
}
}
*keys() {
for (const key of this._map.keys()) {
yield celFromMapKey(this._map.field(), key);
}
}
*values() {
for (const value of this._map.keys()) {
yield celFromMapValue(this._map.field(), value);
}
}
[(_b = privateSymbol, Symbol.iterator)]() {
return this.entries();
}
}
function mapKeyFromCel(desc, v) {
if ((0, uint_js_1.isCelUint)(v)) {
v = v.value;
}
switch (desc.mapKey) {
case protobuf_1.ScalarType.SINT32:
case protobuf_1.ScalarType.INT32:
case protobuf_1.ScalarType.FIXED32:
case protobuf_1.ScalarType.UINT32:
case protobuf_1.ScalarType.SFIXED32:
if (typeof v === "bigint") {
return Number(v);
}
return v;
case protobuf_1.ScalarType.SINT64:
case protobuf_1.ScalarType.INT64:
case protobuf_1.ScalarType.FIXED64:
case protobuf_1.ScalarType.UINT64:
case protobuf_1.ScalarType.SFIXED64:
if (v === "number" && Number.isInteger(v)) {
return BigInt(v);
}
return v;
default:
return v;
}
}
function celFromMapKey(desc, v) {
return (0, proto_js_1.celFromScalar)(desc.mapKey, v);
}
function celFromMapValue(desc, v) {
switch (desc.mapKind) {
case "enum":
return BigInt(v);
case "message":
return (0, value_js_1.reflectMsgToCel)(v);
case "scalar":
return (0, proto_js_1.celFromScalar)(desc.scalar, v);
}
}
exports.EMPTY_MAP = celMap(new Map());