{"id":47707404,"url":"https://github.com/jetstreamapp/sf-formula-parser","last_synced_at":"2026-04-02T18:03:04.392Z","repository":{"id":346206782,"uuid":"1188987439","full_name":"jetstreamapp/sf-formula-parser","owner":"jetstreamapp","description":"Salesforce formula evaluator","archived":false,"fork":false,"pushed_at":"2026-03-22T23:42:32.000Z","size":538,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-23T10:56:28.762Z","etag":null,"topics":["salesforce","salesforce-developer","salesforce-formula"],"latest_commit_sha":null,"homepage":"https://sf-formula-parser.getjetstream.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jetstreamapp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-22T21:04:06.000Z","updated_at":"2026-03-22T23:36:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jetstreamapp/sf-formula-parser","commit_stats":null,"previous_names":["jetstreamapp/sf-formula-parser"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/jetstreamapp/sf-formula-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jetstreamapp%2Fsf-formula-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jetstreamapp%2Fsf-formula-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jetstreamapp%2Fsf-formula-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jetstreamapp%2Fsf-formula-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jetstreamapp","download_url":"https://codeload.github.com/jetstreamapp/sf-formula-parser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jetstreamapp%2Fsf-formula-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["salesforce","salesforce-developer","salesforce-formula"],"created_at":"2026-04-02T18:02:21.262Z","updated_at":"2026-04-02T18:03:04.373Z","avatar_url":"https://github.com/jetstreamapp.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.svg\" alt=\"sf-formula-parser\" width=\"480\" /\u003e\n\u003c/p\u003e\n\n# sf-formula-parser\n\n\u003e Built 100% by [Claude Code](https://claude.com/claude-code) — lexer, parser, evaluator, 90+ functions, tests, and docs.\n\u003e\n\u003e Some tests cases were extracted from [formula-engine](https://github.com/salesforce/formula-engine) to validate actual Salesforce behavior.\n\nA 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.\n\n[Documentation](https://sf-formula-parser.getjetstream.app/)\n\n[npm](https://www.npmjs.com/package/@jetstreamapp/sf-formula-parser)\n\n## Why?\n\nSalesforce 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.\n\nThe implementation was built by studying the [open-source Salesforce formula engine](https://github.com/salesforce/formula-engine) to match real-world behavior, not just documentation.\n\n## Features\n\n- **90+ functions** across logical, math, text, and date/time categories\n- **All operators** — arithmetic (`+`, `-`, `*`, `/`, `^`), comparison (`=`, `!=`, `\u003c`, `\u003e`, `\u003c=`, `\u003e=`), logical (`\u0026\u0026`, `||`, `!`), and string concatenation (`\u0026`)\n- **Case-insensitive** — `IF`, `if`, `If` all work, just like Salesforce\n- **Lazy evaluation** — `IF`, `CASE`, `IFS`, and other branching functions only evaluate the branch that's taken\n- **Related record traversal** — `Account.Name`, `Contact.Account.Industry`\n- **Prior value support** — `ISCHANGED`, `PRIORVALUE`, `ISNEW`, `ISCLONE`\n- **Return type validation** — optionally declare the expected return type (`number`, `string`, `boolean`, `date`, `datetime`, `time`) and get Salesforce-accurate type mismatch errors\n- **Schema-aware validation** — pass `describeSObject().fields` directly to enable field existence checks and picklist restrictions\n- **Strict operator type checking** — arithmetic operators reject booleans and strings, matching Salesforce behavior\n- **Zero dependencies** — pure TypeScript, compiles to ESM\n- **Browser-compatible** — no Node.js APIs required\n\n## Installation\n\n```bash\nnpm install @jetstreamapp/sf-formula-parser\n```\n\n## Quick Start\n\n```typescript\nimport { evaluateFormula } from '@jetstreamapp/sf-formula-parser';\n\nconst result = evaluateFormula('IF(Amount \u003e 1000, \"Large\", \"Small\")', {\n  record: {\n    Amount: 5000,\n  },\n});\n// result: \"Large\"\n```\n\n## API\n\n### `evaluateFormula(formula, context, options?)`\n\nParse and evaluate a formula in one call.\n\n```typescript\nimport { evaluateFormula } from '@jetstreamapp/sf-formula-parser';\n\nconst result = evaluateFormula('UPPER(LEFT(Name, 3))', {\n  record: { Name: 'Acme Corp' },\n});\n// result: \"ACM\"\n```\n\n### `parseFormula(formula)`\n\nParse a formula string into an AST without evaluating it. Useful for caching, inspection, or building tooling on top of formulas.\n\n```typescript\nimport { parseFormula } from '@jetstreamapp/sf-formula-parser';\n\nconst ast = parseFormula('1 + 2 * 3');\n```\n\n### `createEvaluator(registry?, context?, options?)`\n\nCreate a reusable evaluator instance. Useful when evaluating many formulas against the same context or with a custom function registry.\n\n```typescript\nimport { createEvaluator, createDefaultRegistry } from '@jetstreamapp/sf-formula-parser';\n\nconst evaluator = createEvaluator(createDefaultRegistry(), {\n  record: { Status: 'Active', Amount: 100 },\n});\n```\n\n## Record Context\n\nThe `FormulaContext` object represents the record a formula is evaluated against:\n\n```typescript\ninterface FormulaContext {\n  record: FormulaRecord; // field values and related records\n  globals?: Record\u003cstring, FormulaRecord\u003e; // e.g., $User.Id\n  priorRecord?: FormulaRecord; // for ISCHANGED / PRIORVALUE\n  isNew?: boolean; // for ISNEW()\n  isClone?: boolean; // for ISCLONE()\n}\n\n// A flat record — fields and related records coexist as keys, just like a SOQL result\ntype FormulaRecord = { [key: string]: FormulaValue | FormulaRecord };\n```\n\n`FormulaValue` can be `number`, `string`, `boolean`, `Date`, `SfTime`, `GeoLocation`, or `null`.\n\n### Related Records\n\nRelated records are nested directly in the record — no separate `related` property needed:\n\n```typescript\nevaluateFormula('Account.Industry', {\n  record: {\n    Account: { Industry: 'Technology' },\n  },\n});\n// result: \"Technology\"\n```\n\n### Options\n\n```typescript\ninterface EvaluationOptions {\n  returnType?: FormulaReturnType; // validate result type ('number' | 'string' | 'boolean' | 'date' | 'datetime' | 'time')\n  schema?: SchemaInput; // flat FieldSchema[] or Record\u003cstring, FieldSchema[]\u003e for related/global schemas\n  treatBlanksAsZeroes?: boolean; // default: true (matches Salesforce default)\n  now?: Date; // override current time for deterministic tests\n}\n```\n\n### Return Type Validation\n\nDeclare the expected return type to catch type mismatches, just like Salesforce:\n\n```typescript\n// Passes — formula returns a number\nevaluateFormula('Amount + 1', { record: { Amount: 100 } }, { returnType: 'number' });\n\n// Throws — formula returns a Date, but number was expected\nevaluateFormula('CreatedDate + 1', { record: { CreatedDate: new Date() } }, { returnType: 'number' });\n// FormulaError: Formula result is data type (Date), incompatible with expected data type (Number).\n```\n\n### Schema Validation\n\nPass Salesforce field metadata for field existence checks and picklist restrictions:\n\n```typescript\nimport { evaluateFormula } from '@jetstreamapp/sf-formula-parser';\n\n// Fields from describeSObject() — pass directly, no transformation needed\nconst schema = [\n  { name: 'Name', type: 'string' },\n  { name: 'Amount', type: 'currency' },\n  { name: 'Status', type: 'picklist' },\n];\n\n// Field existence check\nevaluateFormula('MissingField', { record: {} }, { schema });\n// FormulaError: Field MissingField does not exist. Check spelling.\n\n// Picklist restriction (matches Salesforce behavior)\nevaluateFormula('Status \u0026 \" test\"', { record: { Status: 'Active' } }, { schema });\n// FormulaError: Field status is a picklist field. Picklist fields are only supported in certain functions.\n\n// Use ISPICKVAL instead\nevaluateFormula('ISPICKVAL(Status, \"Active\")', { record: { Status: 'Active' } }, { schema });\n// true\n```\n\n#### Related Object \u0026 Global Schema\n\nPass a `Record\u003cstring, FieldSchema[]\u003e` to validate related object fields and globals too. Use `'$record'` for the root object, relationship names for related objects, and `$`-prefixed names for globals:\n\n```typescript\nconst schema = {\n  $record: describeContact.fields, // current object (Contact)\n  Account: describeAccount.fields, // Account relationship\n  $User: describeUser.fields, // $User global\n};\n\nevaluateFormula('Account.Name', context, { schema }); // validated against Account schema\nevaluateFormula('$User.FirstName', context, { schema }); // validated against $User schema\nevaluateFormula('Account.MissingField', context, { schema });\n// FormulaError: Field MissingField does not exist. Check spelling.\n```\n\nRelationships not included in the schema map bypass validation — you only need to provide schemas for the objects you want validated.\n\n## Supported Functions\n\n| Category           | Functions                                                                                                                                                                                                                                                                                 |\n| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Logical** (16)   | `IF`, `IFS`, `CASE`, `AND`, `OR`, `NOT`, `ISBLANK`, `ISNULL`, `ISNUMBER`, `BLANKVALUE`, `NULLVALUE`, `IFERROR`, `ISCHANGED`, `ISNEW`, `ISCLONE`, `PRIORVALUE`                                                                                                                             |\n| **Math** (19)      | `ABS`, `CEILING`, `FLOOR`, `MCEILING`, `MFLOOR`, `EXP`, `LN`, `LOG`, `MAX`, `MIN`, `MOD`, `PI`, `POWER`, `RAND`, `ROUND`, `SQRT`, `TRUNC`, `DISTANCE`, `GEOLOCATION`                                                                                                                      |\n| **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` |\n| **Date/Time** (17) | `ADDMONTHS`, `DATE`, `DATEVALUE`, `DATETIMEVALUE`, `DAY`, `HOUR`, `MILLISECOND`, `MINUTE`, `MONTH`, `NOW`, `SECOND`, `TIMENOW`, `TIMEVALUE`, `TODAY`, `WEEKDAY`, `YEAR`                                                                                                                   |\n\n## Architecture\n\n```\nFormula String → Lexer → Tokens → Parser → AST → Evaluator → FormulaValue\n                                                      ↑\n                                              FormulaContext + Options\n```\n\n- **Lexer** — tokenizes the formula string, handling string escapes, comments, and operators\n- **Parser** — hand-rolled Pratt parser (top-down operator precedence) producing a discriminated union AST\n- **Evaluator** — tree-walking evaluator with lazy argument evaluation and a pluggable function registry\n\n## Error Handling\n\nThe library throws typed errors you can catch and inspect:\n\n```typescript\nimport { FormulaError, LexerError, ParseError } from '@jetstreamapp/sf-formula-parser';\n\ntry {\n  evaluateFormula('IF(', { record: {} });\n} catch (e) {\n  if (e instanceof ParseError) {\n    // syntax error\n  } else if (e instanceof FormulaError) {\n    // runtime evaluation error\n  }\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjetstreamapp%2Fsf-formula-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjetstreamapp%2Fsf-formula-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjetstreamapp%2Fsf-formula-parser/lists"}