aer 1.4

aer 1.4 extends every part of the local Apex workflow: the test runner reports coverage per test and runs test suites, the development server accepts deployments of any metadata type and can store its data in PostgreSQL, the VS Code extension reads your project configuration from sfdx-project.json, and default flags can be kept in an .aerrc file.

Per-test code coverage

sfapex, Salesforce’s own Apex implementation, records which test method covered which lines of each class. aer now reports the same:

aer test force-app --json results.jsonl --coverage-per-test

Each test record carries a perClassCoverage field listing the covered and uncovered lines of every class the test touched. The coverage JSON written by --coverage also lists each class’s covered lines in a coveredLines field alongside the uncovered lines.

In VS Code, a new Run Apex Tests with Coverage profile in the Testing view runs the selected tests with coverage and shows covered and uncovered lines in the editor gutter and the Test Coverage view.

Test suites and other test runner additions

  • Test suites. aer test --suite <name> runs the classes named by a .testSuite-meta.xml file in your source. The suites are queryable as ApexTestSuite and TestSuiteMembership records, and sf apex run test --suite-names works against the development server.
  • Permission set groups. --assign-psg assigns permission set groups to the default user, the counterpart of --assign-perms.
  • Deployment-style validation. --skip-mixed-dml-validation turns off the MIXED_DML_OPERATION check, matching how sfapex runs tests during a deployment.
  • Live status. Sending SIGUSR1 to a running aer test process prints a one-line status summary, including the setup phase and elapsed time before tests start.
  • Faster reruns. A rerun over unchanged sources restores a cached workspace image and skips parsing, semantic analysis, type checking, and symbol resolution.
  • Packages as source. A .pkg file passed as a source path, or left inside a source directory, loads as an unmanaged package with no --package flag. Its test classes are discovered and run like your own.

VS Code reads sfdx-project.json

The VS Code extension now derives its configuration from sfdx-project.json. Source paths come from packageDirectories, the default namespace from namespace, and replacements are applied by staging source into a temporary directory before running, matching what sf project deploy start would deploy. Each package directory’s unpackagedMetadata is loaded alongside the packaged source, and apexTestAccess.permissionSets are passed to aer as --assign-perms.

The language server, test runner, watch mode, and debugger all use these settings, and failure locations in staged files map back to the real files. The aer.useSfdxProject setting is on by default; explicit aer.sourcePaths and aer.defaultNamespace settings still take precedence.

Development server

aer server exists so you can preview Lightning Web Components and UI Bundles, and exercise the Salesforce APIs from scripts and integration tests, without deploying to an org. This release makes it easier to iterate against the server and widens the APIs it answers.

Deploy any metadata type without restarting

The server’s deploy endpoints previously handled Apex classes, single-file custom objects, and a narrow set of destructive changes. Deployments are now imported with the same metadata walker aer uses at startup, so a standard deployment from the Salesforce CLI can add or change objects and fields, triggers, flows, flexipages, workflow rules, permission sets, profiles, custom metadata records, queues, labels, value sets, static resources, Visualforce pages, email templates, reports, and dashboards while the server keeps running:

sf project deploy start --source-dir force-app -o aer
sf project deploy start --source-dir force-app -o aer --dry-run

Each deployment compiles Apex against the merged schema, validates package.xml members the way the Metadata API does, and rolls back on failure. A deployment that changes only Apex no longer triggers a schema reload.

Endpoint browser

The landing page is now an endpoint browser. It lists every development tool and API endpoint the server exposes, grouped by category, with a search box (press / to focus it), a copy button for each path, and a status indicator that shows whether the server is still responding. The endpoint list is built from the live routes, so Apex REST classes from your source appear alongside the built-in endpoints. The preview, monitoring, and explorer pages share the same design with a light and dark theme.

More of the API surface

  • Tooling API. MetadataContainer, ApexClassMember, and ContainerAsyncRequest implement the container save path IDE integrations use, MetadataComponentDependency queries come from aer’s dependency graph, and runTestsSynchronous runs a test class and records coverage in ApexCodeCoverage.
  • Analytics API. analytics/reports and analytics/dashboards run reports and dashboards from your source and return factMap-shaped results, so code that consumes report data can be tested locally. The report engine behind them gained row-level formulas, joined reports, bucket fields, activity reports, and custom report types.
  • UI Bundles. The Data SDK shim resolves @salesforce/platform-sdk, and bundles are served at the GA /app/c__<bundleName> URL.
  • REST and Bulk writes run the full DML pipeline. A record written through those endpoints fires the same triggers, flows, workflow rules, and validation rules an Apex insert would, and each row reports its own errors.
  • Pre-shared session tokens. --seed-session-token registers a token as an authenticated session at startup, so a script or CI job can log in with sf org login access-token without a browser.
  • The default API version is now 67.0.

PostgreSQL storage

The exec, test, and server commands accept --db postgres://… to store data in a PostgreSQL database instead of the embedded SQLite store, and --db postgresqltest:// starts a private, disposable PostgreSQL server for the run. The schema uses identity columns and PL/pgSQL triggers, the formula and SOQL functions are installed as database functions, and text comparison uses a collation that matches sfapex. SELECT … FOR UPDATE takes row locks in the database, and a lock timeout raises a QueryException with UNABLE_TO_LOCK_ROW, the same error sfapex raises.

Keeping data between sessions

A server started with --db keeps its data between runs. Several changes make that smoother. A restart against an unchanged project attaches to the existing database without re-migrating tables or reseeding metadata. Upgrading aer no longer requires rebuilding the database. --bootstrap-db merges into the existing data rather than wiping it first. Session tokens are stored in the database, so a login survives a restart.

Reliability under concurrent requests

A page that makes many requests at once, or an integration test that runs in parallel, exercised bugs that are now fixed. Apex REST requests leaked pooled VMs until the pool was exhausted and the server stopped responding. Concurrent requests shared a storage transaction, which produced 500s and lost writes. A deployment could panic against in-flight execution. Each has an explicit fix, and the async job worker now wakes only when async work was actually enqueued.

Configuration

  • .aerrc files hold default flags so they need not be repeated on every invocation. Files are read from $XDG_CONFIG_HOME/aer/aerrc, ~/.aerrc, and ./.aerrc, in increasing precedence. An explicit command-line flag wins, and a flag not defined on the invoked command is dropped, so one file can hold defaults for several commands. aer --help-aerrc explains the format.
  • --locale and --currency set the org’s default locale and currency, alongside the existing --timezone.
  • --allow-email delivers Messaging.sendEmail over SMTP. Without the flag, email is validated and logged but not sent, as before.

Platform coverage

  • Revenue Cloud. The full Agentforce Revenue Management SObject suite is modeled behind the RevenueCloud feature, including the field derivations and status restrictions sfapex enforces on Order, FulfillmentOrder, and accounting periods.
  • Flows. A record-triggered flow now saves once per element rather than once per record, matching the Flow runtime’s bulkification. Transform elements, REGEX in formulas, $Flow.CurrentDate typing, and null handling were also corrected. The changelog lists 37 Flow fixes.
  • Approval processes. An approval process runs from your approvalProcesses metadata, and each step’s entry criteria now decide whether a submitted record enters that step: a record that fails them skips the step, so an approver field guarded by a criterion no longer fails the submission, and running out of steps completes the approval. An approval’s field updates save through the DML pipeline, so the object’s triggers, workflow rules, and record-triggered flows run. Submitting a record that already has a pending approval fails with ALREADY_IN_PROCESS, and Approval.ProcessWorkitemRequest rejects an unknown or null action rather than treating it as a successful transition. Workflow and approval criteria also gained the relational operations, which previously evaluated false and silently disabled every rule resting on them.
  • Duplicate rules. Duplicate rules are enforced on insert and update DML, with the fuzzy name, company, and address matching algorithms. A rule now participates in an update only when that update changes a field its matching rule matches on, so editing an unrelated field no longer fails the save with DUPLICATES_DETECTED over a duplicate the record already had.
  • Test data isolation. A test without SeeAllData no longer sees rows it did not write. A committed org row, from a bootstrap database or seeded metadata, is hidden from SOQL, aggregates, query locators, SOSL, semi-joins, child subqueries, and duplicate-rule matching, while staying addressable for insert, update, and delete. Setup and metadata objects stay visible.
  • Fixes. The changelog lists 169 fixes in this release, covering SOQL null semantics, formula field inlining, workflow and approval criteria, LWC compilation, person accounts, JSON parsing, Visualforce components, and coverage accounting. If aer behaved differently from Salesforce on something you rely on, check the CHANGELOG before working around it.

Upgrading

brew upgrade aer
# or
aer upgrade

If the Salesforce CLI plugin downloaded its own copy of aer, it checks for a newer release on a later run and offers to install it. sf plugins update updates the plugin itself, not that copy. The VS Code extension updates through the Marketplace. Its minimum VS Code version is now 1.88.0, where the test coverage API was finalized.

See Development Server, Configuration, and Interactive Debugging for the documentation of these features.

© 2012–2026 October Swimmer.