Skip to content

feat: Replace formulon with sf-formula-parser for formula evaluation#1607

Open
paustint wants to merge 1 commit intomainfrom
feat/replace-formula-evaluator
Open

feat: Replace formulon with sf-formula-parser for formula evaluation#1607
paustint wants to merge 1 commit intomainfrom
feat/replace-formula-evaluator

Conversation

@paustint
Copy link
Copy Markdown
Contributor

Formulon had partial support and had a number of bugs and issues with Salesforce formulas We created a new open-source library with much better salesforce formula coverage

Copilot AI review requested due to automatic review settings March 23, 2026 01:58
@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 23, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​jetstreamapp/​sf-formula-parser@​2.1.0761009993100

View full report

@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 23, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates formula evaluation from the formulon library to the newer @jetstreamapp/sf-formula-parser, aiming to improve Salesforce formula coverage and reliability across the Formula Evaluator and formula editor workflows.

Changes:

  • Replace formulon parsing/evaluation with extractFields* + evaluateFormula from @jetstreamapp/sf-formula-parser.
  • Refactor formula context building to the new FormulaContext shape (record + globals) and update UI rendering accordingly.
  • Add Playwright E2E coverage for the formula evaluator (including globals and error cases) and supporting page object/fixture wiring.

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
yarn.lock Adds @jetstreamapp/sf-formula-parser and removes formulon transitive lock entries.
package.json Swaps dependencies: adds @jetstreamapp/sf-formula-parser, removes formulon.
libs/shared/ui-core/src/formula-evaluator/formula-evaluator.utils.ts Reworks data collection to build FormulaContext (record + globals) for the new evaluator.
libs/shared/ui-core/src/formula-evaluator/FormulaEvaluatorResults.tsx Updates results rendering to show the new result + flattens FormulaContext for display; adds test ids.
libs/features/formula-evaluator/src/FormulaEvaluator.tsx Switches evaluator flow to sf-formula-parser and passes null/blank behavior via options.
libs/features/create-object-and-fields/src/CreateFieldsFormulaEditor.tsx Switches formula testing from formulon to sf-formula-parser with categorized fields + context.
libs/test/e2e-utils/src/lib/pageObjectModels/FormulaEvaluatorPage.model.ts New Playwright page object model for the Formula Evaluator.
libs/test/e2e-utils/src/index.ts Exports the new FormulaEvaluator page model.
apps/jetstream-e2e/src/tests/formula-evaluator/formula-evaluator.spec.ts New E2E test suite covering formula evaluation behavior and errors.
apps/jetstream-e2e/src/fixtures/fixtures.ts Adds formulaEvaluatorPage fixture wiring.
formulon.d.ts Removes custom typings for formulon (no longer used).
.dockerignore Removes formulon.d.ts exception (file deleted).
Comments suppressed due to low confidence (1)

libs/shared/ui-core/src/formula-evaluator/formula-evaluator.utils.ts:157

  • getFormulaData declares it can return { type: 'error' }, and callers branch on response.type === 'error', but the implementation always throws in the catch block and never returns the error variant. This makes the error-handling branch unreachable and shifts expected “field error” cases into the generic exception path. Either change getFormulaData to return { type: 'error', message } for expected failures (and avoid throwing), or update the return type + callers to treat it as throwing-only.
}: FormulaDataProps | FormulaDataProvidedRecordProps): Promise<
  { type: 'error'; message: string } | { type: 'success'; context: FormulaContext; warnings: { type: string; message: string }[] }
> {
  try {
    const formulaRecord: FormulaRecord = {};
    const globals: Record<string, FormulaRecord> = {};
    const warnings: { type: string; message: string }[] = [];

    const { objectFields, globals: globalFields, customMetadata, customLabels, customSettings, customPermissions } = categorizedFields;

    const userFields = globalFields['$User'] || [];
    const organizationFields = globalFields['$Organization'] || [];
    const profileFields = globalFields['$Profile'] || [];
    const userRoleFields = globalFields['$UserRole'] || [];
    const apiFields = globalFields['$Api'] || [];
    const systemFields = globalFields['$System'] || [];

    if (type === 'QUERY_RECORD' && recordId) {
      await collectBaseQueriedRecordFields({ selectedOrg, fields: objectFields, recordId, sobjectName, output: formulaRecord });
    } else {
      collectBaseRecordFields({ fields: objectFields, manualRecord: record || {}, output: formulaRecord });
    }

    collectApiFields({ selectedOrg, fields: apiFields, globals });
    await collectCustomMetadata({ selectedOrg, fields: customMetadata, globals });
    await collectCustomSettingFields({ selectedOrg, selectedUserId, fields: customSettings, globals });
    await collectCustomPermissions({ selectedOrg, selectedUserId, fields: customPermissions, globals });
    await collectLabels({ selectedOrg, fields: customLabels, globals });
    await collectOrganizationFields({ selectedOrg, fields: organizationFields, globals });
    await collectUserProfileAndRoleFields({
      selectedOrg,
      selectedUserId,
      userFields,
      profileFields,
      roleFields: userRoleFields,
      globals,
    });
    collectSystemFields({ fields: systemFields, globals });

    const context: FormulaContext = { record: formulaRecord };
    if (Object.keys(globals).length) {
      context.globals = globals;
    }

    logger.log({ context, warnings });

    return { type: 'success', context, warnings };
  } catch (ex) {
    logger.error(ex);
    throw ex;
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@paustint paustint force-pushed the feat/replace-formula-evaluator branch 2 times, most recently from 8b94314 to 1fea415 Compare March 25, 2026 14:21
@paustint paustint requested a review from Copilot March 25, 2026 14:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 18 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Formulon had partial support and had a number of bugs and issues with Salesforce formulas
We created a new open-source library with much better salesforce formula coverage
@paustint paustint force-pushed the feat/replace-formula-evaluator branch from 1fea415 to 3abab4d Compare March 26, 2026 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants