Production-grade TypeScript implementation of the Common Expression Language (CEL).
- 100% Conformance Test Coverage - Full CEL conformance suite coverage
- Type Checking - Full compile-time type checking
- Full Macro Support - cel-go parity macros (including
cel.blocketc.) - Full Extension Support - cel-go parity extension packs
- Proto Conversion (cel.expr) - Round‑trip conversions to and from CEL protobufs
- cel-go Compatible API - Mirrors cel-go's clean API
- Zero Runtime Dependencies - Avoids deprecated packages and supply‑chain risk
- Licence Alignment - Apache-2.0, compatible with cel-spec
ChromeGG/cel-js is a popular TypeScript CEL interpreter, but it misses baseline capabilities such as type checking, environment configuration, conformance coverage, and tooling that official implementations treat as standard.
marcbachmann/cel-js is another JavaScript CEL interpreter. It emphasises fast evaluation and type safety, but it does not cover the full CEL feature set or pass the conformance test suite.
Both ChromeGG/cel-js and marcbachmann/cel-js are MIT licensed without explicit notice, yet they derive from official implementations and incorporate parts of cel-spec artifacts (e.g., langdef.md).
taichimaeda/cel-ts tracks official CEL implementations (cel-go, cel-cpp, cel-java) and provides the complete workflow: parsing, type checking, optimisation, evaluation, conformance tests, and tooling. It also keeps Apache-2.0 licensing aligned with cel-spec, unlike the MIT-licensed alternatives above. If you need a production-grade CEL engine in the JS/TS ecosystem, cel-ts is the practical choice.
cel-ts is consistently faster than ChromeGG/cel-js in the benchmark suite and generally close to marcbachmann/cel-js in throughput. The benchmarks also include cel-go-wasm, which runs cel-go compiled to WebAssembly via the Go runtime bridge.
| Item | taichimaeda/cel-ts | ChromeGG/cel-js | marcbachmann/cel-js |
|---|---|---|---|
| Conformance tests | Yes (100%) | No | No |
| Benchmarking | Yes | No | Yes |
| Fuzzing | Yes (parser only) | No | No |
| Profiling | Yes | No | No |
| Type checking | Full | No | Basic |
| Macro support | Full (same as cel-go) | Limited | Limited |
| Extension packs | Full (same as cel-go) | Limited | Limited |
| Protobuf conversion | Yes (protobuf-es) | No | No |
| Raw string and byte literals | Yes | No | No |
| Runtime dependencies | Zero | Few | Zero |
| Formatter | Yes | No | No |
| Linter | Yes | No | No |
| Speed | Fast | Slow | Fast |
| Licence | Apache-2.0 | MIT | MIT |
| Case | taichimaeda/cel-ts (avg ns) | ChromeGG/cel-js (avg ns) | marcbachmann/cel-js (avg ns) | cel-go-wasm (avg ns) |
|---|---|---|---|---|
| string_eq | 107.78 | 448.48 | 103.99 | 8294.11 |
| string_neq | 106.35 | 330.15 | 60.18 | 7479.14 |
| value_in_list_value | 165.32 | 342.13 | 152.40 | 8904.42 |
| value_not_in_list_value | 160.94 | 399.85 | 105.03 | 8549.92 |
| x_in_literal_list | 136.19 | 552.12 | 147.89 | 7700.65 |
| x_not_in_literal_list | 144.71 | 618.56 | 107.49 | 7623.88 |
| x_in_list_value | 219.63 | 356.24 | 147.89 | 9406.11 |
| x_not_in_list_value | 226.22 | 421.20 | 126.39 | 9489.88 |
| list_exists_contains | 504.29 | - | 387.82 | 10718.48 |
| list_exists_starts | 403.43 | - | 388.04 | 10713.10 |
| list_exists_matches | 285.47 | - | 202.72 | 13211.36 |
| list_filter_matches | 601.03 | - | 795.72 | 24097.92 |
This package is not published yet, but will be available soon.
npm install cel-ts
# or
pnpm add cel-tsimport * as cel from "cel-ts";
// Create an environment with variable declarations
const env = new cel.Env({
variables: [
new cel.Variable("name", cel.StringType),
new cel.Variable("age", cel.IntType),
],
});
// Compile an expression
const ast = env.compile('name + " is " + string(age) + " years old"');
// Create a program
const program = env.program(ast);
// Evaluate with variables
const result = program.eval({
name: "Alice",
age: 30,
});
console.info(result.value()); // "Alice is 30 years old"Additional runnable snippets are available under examples/README.md. Execute them with bun or your
preferred TS runner, e.g.:
bun run examples/basic.tsFormatting and linting examples live in examples/formatting.ts and examples/linting.ts.
Run pnpm docs to generate TypeDoc output under docs/api. Use pnpm docs:view to open the generated docs locally. Online documentation is also available at https://taichimaeda.github.io/cel-ts/.
pnpm install # Install dependencies
pnpm dev # Watch and rebuild on changes
pnpm typecheck # Run TypeScript type checks
pnpm lint # Run lint rules
pnpm lint:fix # Auto-fix lint + format issues
pnpm format # Format code with Biome
pnpm test # Run unit tests
pnpm test:watch # Run tests in watch mode
pnpm test:coverage # Run tests with coverage
pnpm conformance # Run CEL conformance suite
pnpm conformance:serve # Run conformance with HTML report
pnpm benchmark # Run benchmarks (writes results.json)
pnpm fuzz # Run parser fuzzing
pnpm profile:build # Build profiling runner
pnpm profile:serve # Run clinic flamegraph profiling
pnpm build # Build package artifacts
pnpm docs # Build TypeDoc output (same as docs:build)
pnpm docs:build # Build TypeDoc output
pnpm docs:watch # Watch and rebuild docs
pnpm docs:view # Build and open docs in browser
pnpm docs:serve # Alias for docs:view
pnpm clean # Remove build/test artefacts
pnpm proto:gen # Generate protobuf outputsConformance suites from cel.dev/expr live under test/conformance/ and can be run via:
pnpm conformanceRun the lightweight benchmark suite and write results to test/benchmark/results.json:
pnpm benchmarkGenerate fuzz cases under test/fuzz/cases and compare parsing behavior with cel-go:
pnpm fuzzApache-2.0 Licence (same as cel-go)
See CONTRIBUTING.md.
This project began as a TypeScript port of cel-go, which was originally developed by Google.