npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jetstreamapp/sf-formula-parser

v2.0.0

Published

Salesforce formula parser and evaluator — parse and execute Salesforce formula expressions in JavaScript

Readme

sf-formula-parser

Built 100% by Claude Code — lexer, parser, evaluator, 90+ functions, tests, and docs.

Some tests cases were extracted from formula-engine to validate actual Salesforce behavior.

A JavaScript/TypeScript implementation of the Salesforce formula language. Parses formula strings into an AST and evaluates them against a record context — entirely client-side, with zero dependencies.

Documentation

npm

Why?

Salesforce formulas are powerful, but evaluating them outside of Salesforce (in custom UIs, offline tools, testing harnesses, or migration scripts) has historically required either round-tripping to the API or reimplementing logic by hand. This library gives you a spec-faithful formula engine that runs anywhere JavaScript runs — browsers, Node.js, edge functions.

The implementation was built by studying the open-source Salesforce formula engine to match real-world behavior, not just documentation.

Features

  • 90+ functions across logical, math, text, and date/time categories
  • All operators — arithmetic (+, -, *, /, ^), comparison (=, !=, <, >, <=, >=), logical (&&, ||, !), and string concatenation (&)
  • Case-insensitiveIF, if, If all work, just like Salesforce
  • Lazy evaluationIF, CASE, IFS, and other branching functions only evaluate the branch that's taken
  • Related record traversalAccount.Name, Contact.Account.Industry
  • Prior value supportISCHANGED, PRIORVALUE, ISNEW, ISCLONE
  • Return type validation — optionally declare the expected return type (number, string, boolean, date, datetime, time) and get Salesforce-accurate type mismatch errors
  • Schema-aware validation — pass describeSObject().fields directly to enable field existence checks and picklist restrictions
  • Strict operator type checking — arithmetic operators reject booleans and strings, matching Salesforce behavior
  • Zero dependencies — pure TypeScript, compiles to ESM
  • Browser-compatible — no Node.js APIs required

Installation

npm install @jetstreamapp/sf-formula-parser

Quick Start

import { evaluateFormula } from '@jetstreamapp/sf-formula-parser';

const result = evaluateFormula('IF(Amount > 1000, "Large", "Small")', {
  record: {
    Amount: 5000,
  },
});
// result: "Large"

API

evaluateFormula(formula, context, options?)

Parse and evaluate a formula in one call.

import { evaluateFormula } from '@jetstreamapp/sf-formula-parser';

const result = evaluateFormula('UPPER(LEFT(Name, 3))', {
  record: { Name: 'Acme Corp' },
});
// result: "ACM"

parseFormula(formula)

Parse a formula string into an AST without evaluating it. Useful for caching, inspection, or building tooling on top of formulas.

import { parseFormula } from '@jetstreamapp/sf-formula-parser';

const ast = parseFormula('1 + 2 * 3');

createEvaluator(registry?, context?, options?)

Create a reusable evaluator instance. Useful when evaluating many formulas against the same context or with a custom function registry.

import { createEvaluator, createDefaultRegistry } from '@jetstreamapp/sf-formula-parser';

const evaluator = createEvaluator(createDefaultRegistry(), {
  record: { Status: 'Active', Amount: 100 },
});

Record Context

The FormulaContext object represents the record a formula is evaluated against:

interface FormulaContext {
  record: FormulaRecord; // field values and related records
  globals?: Record<string, FormulaRecord>; // e.g., $User.Id
  priorRecord?: FormulaRecord; // for ISCHANGED / PRIORVALUE
  isNew?: boolean; // for ISNEW()
  isClone?: boolean; // for ISCLONE()
}

// A flat record — fields and related records coexist as keys, just like a SOQL result
type FormulaRecord = { [key: string]: FormulaValue | FormulaRecord };

FormulaValue can be number, string, boolean, Date, SfTime, GeoLocation, or null.

Related Records

Related records are nested directly in the record — no separate related property needed:

evaluateFormula('Account.Industry', {
  record: {
    Account: { Industry: 'Technology' },
  },
});
// result: "Technology"

Options

interface EvaluationOptions {
  returnType?: FormulaReturnType; // validate result type ('number' | 'string' | 'boolean' | 'date' | 'datetime' | 'time')
  schema?: SchemaInput; // flat FieldSchema[] or Record<string, FieldSchema[]> for related/global schemas
  treatBlanksAsZeroes?: boolean; // default: true (matches Salesforce default)
  now?: Date; // override current time for deterministic tests
}

Return Type Validation

Declare the expected return type to catch type mismatches, just like Salesforce:

// Passes — formula returns a number
evaluateFormula('Amount + 1', { record: { Amount: 100 } }, { returnType: 'number' });

// Throws — formula returns a Date, but number was expected
evaluateFormula('CreatedDate + 1', { record: { CreatedDate: new Date() } }, { returnType: 'number' });
// FormulaError: Formula result is data type (Date), incompatible with expected data type (Number).

Schema Validation

Pass Salesforce field metadata for field existence checks and picklist restrictions:

import { evaluateFormula } from '@jetstreamapp/sf-formula-parser';

// Fields from describeSObject() — pass directly, no transformation needed
const schema = [
  { name: 'Name', type: 'string' },
  { name: 'Amount', type: 'currency' },
  { name: 'Status', type: 'picklist' },
];

// Field existence check
evaluateFormula('MissingField', { record: {} }, { schema });
// FormulaError: Field MissingField does not exist. Check spelling.

// Picklist restriction (matches Salesforce behavior)
evaluateFormula('Status & " test"', { record: { Status: 'Active' } }, { schema });
// FormulaError: Field status is a picklist field. Picklist fields are only supported in certain functions.

// Use ISPICKVAL instead
evaluateFormula('ISPICKVAL(Status, "Active")', { record: { Status: 'Active' } }, { schema });
// true

Related Object & Global Schema

Pass a Record<string, FieldSchema[]> to validate related object fields and globals too. Use '$record' for the root object, relationship names for related objects, and $-prefixed names for globals:

const schema = {
  $record: describeContact.fields, // current object (Contact)
  Account: describeAccount.fields, // Account relationship
  $User: describeUser.fields, // $User global
};

evaluateFormula('Account.Name', context, { schema }); // validated against Account schema
evaluateFormula('$User.FirstName', context, { schema }); // validated against $User schema
evaluateFormula('Account.MissingField', context, { schema });
// FormulaError: Field MissingField does not exist. Check spelling.

Relationships not included in the schema map bypass validation — you only need to provide schemas for the objects you want validated.

Supported Functions

| Category | Functions | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Logical (16) | IF, IFS, CASE, AND, OR, NOT, ISBLANK, ISNULL, ISNUMBER, BLANKVALUE, NULLVALUE, IFERROR, ISCHANGED, ISNEW, ISCLONE, PRIORVALUE | | Math (19) | ABS, CEILING, FLOOR, MCEILING, MFLOOR, EXP, LN, LOG, MAX, MIN, MOD, PI, POWER, RAND, ROUND, SQRT, TRUNC, DISTANCE, GEOLOCATION | | Text (27) | BEGINS, BR, CASESAFEID, CONTAINS, FIND, GETSESSIONID, HTMLENCODE, HYPERLINK, IMAGE, INCLUDES, ISPICKVAL, JSENCODE, JSINHTMLENCODE, LEFT, LEN, LOWER, LPAD, MID, RIGHT, RPAD, SUBSTITUTE, TEXT, TRIM, UPPER, URLENCODE, VALUE, REGEX | | Date/Time (17) | ADDMONTHS, DATE, DATEVALUE, DATETIMEVALUE, DAY, HOUR, MILLISECOND, MINUTE, MONTH, NOW, SECOND, TIMENOW, TIMEVALUE, TODAY, WEEKDAY, YEAR |

Architecture

Formula String → Lexer → Tokens → Parser → AST → Evaluator → FormulaValue
                                                      ↑
                                              FormulaContext + Options
  • Lexer — tokenizes the formula string, handling string escapes, comments, and operators
  • Parser — hand-rolled Pratt parser (top-down operator precedence) producing a discriminated union AST
  • Evaluator — tree-walking evaluator with lazy argument evaluation and a pluggable function registry

Error Handling

The library throws typed errors you can catch and inspect:

import { FormulaError, LexerError, ParseError } from '@jetstreamapp/sf-formula-parser';

try {
  evaluateFormula('IF(', { record: {} });
} catch (e) {
  if (e instanceof ParseError) {
    // syntax error
  } else if (e instanceof FormulaError) {
    // runtime evaluation error
  }
}

License

MIT