@simplysf/simply-core
v1.4.0
Published
Shared internal utilities for SimplySF Salesforce CLI plugins
Readme
@simplysf/simply-core
Shared internal utilities for @simplysf Salesforce CLI plugins. This is not a Salesforce CLI plugin itself — it's a plain library consumed by the other packages in this monorepo.
It deliberately does not depend on @salesforce/sf-plugins-core. Anything needing the oclif flag or command layer belongs in @simplysf/simply-plugin-kit instead.
Install
npm install @simplysf/simply-coreAPI
Everything below is exported from the package root.
Authentication
| Export | Description |
| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| authenticateClientCredentials(options) | Authenticates via the OAuth 2.0 Client Credentials grant — a flow the Salesforce CLI has no built-in support for (only web, JWT, and SFDX auth-url). Proxies @jsforce/jsforce-node's OAuth2 token exchange into @salesforce/core's AuthInfo, the same way the JWT flow hands off its own token internally, so the resulting org is persisted and alias-able like any other sf-authenticated org. |
| ClientCredentialsAuthOptions, ClientCredentialsAuthResult | Option and result types. |
import { authenticateClientCredentials } from '@simplysf/simply-core';
const { username } = await authenticateClientCredentials({
loginUrl: 'https://my-org.my.salesforce.com',
consumerKey: 'consumer-key',
consumerSecret: 'consumer-secret', // or consumerSecretFile: './consumer-secret.txt'
alias: 'my-org',
setDefault: true,
});
// -> persisted, usable as --target-org my-org anywhere in the sf/simply ecosystemThe Connected App's OAuth policy must include the api and id/openid scopes — AuthInfo resolves the username/org ID via services/oauth2/userinfo, which depends on them. Client Credentials tokens run as the single "run as" user configured on the Connected App in Setup, not a per-request user.
Querying
| Export | Description |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| queryRecords(conn, soql, options?) | Async generator yielding flat, string-valued records. Picks its own transport: below bulkThreshold (default 2000) it uses Connection#autoFetchQuery; above it, streamBulkQuery, so memory stays flat at any result size. Not for Tooling API queries — Bulk API v2 doesn't support them. |
| chunkedInQuery(conn, values, buildSoql, options) | Runs a query once per chunk of values, splicing each chunk into a quoted IN (...) clause, and returns every record from every chunk. Use when an IN list would otherwise exceed SOQL's length limit. |
| escapeSoqlLiteral(value) | Escapes a value for use inside a single-quoted SOQL string literal. |
| ChunkedInQueryOptions, QueryRecordsOptions | Option types for the two query helpers. |
Bulk API v2 streaming export
| Export | Description |
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| streamBulkQuery(conn, soql, opts?) | Runs a SOQL query through Bulk API v2 and returns the merged CSV results as one Readable. |
| streamBulkQueryToFile(conn, soql, path, opts?) | The same, written straight to a file — the common case. |
| SkipFirstLineTransform | Strips the duplicate header row from each result page after the first. |
| StreamBulkQueryOptions, StreamBulkQueryResult, StreamBulkQueryToFileOptions, StreamBulkQueryToFileResult | Option and result types. |
Connection.bulk2.query() (jsforce's own convenience method) routes each result page through jsforce's legacy HTTP transport, which fully buffers each page into memory before your code can consume it — defeating streaming for large exports. These helpers avoid that by fetching result pages directly with undici, converting the response body straight to a Node stream, and piping it onward. jsforce is still used for job creation and polling; only the result-page fetch bypasses it.
import { streamBulkQueryToFile } from '@simplysf/simply-core';
const result = await streamBulkQueryToFile(connection, 'SELECT Id, Name FROM Account', './accounts.csv');
// { jobId: '750...', numberRecordsProcessed: 42 }CSV output
| Export | Description |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| createCsvFileWriter(outputPath, columns) | Opens a streaming CSV writer, for writing rows incrementally without holding them all in memory. |
| writeRecordsToCsvFile(records, outputPath, columns) | Pipes an AsyncIterable of records — e.g. queryRecords() — straight to a CSV file. Resolves to { recordCount }. |
| CsvFileWriter | The writer handle returned by createCsvFileWriter. |
sfdx-project.json
| Export | Description |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| readSfdxProject(dir?) | Reads and parses the project file. |
| getDefaultPackageDirectory(project) | Returns the entry flagged default: true. |
| getPluginConfig(source, keyPath) | Walks a dot-delimited path (e.g. plugins.simply.dependencies.ignore) through any object, returning undefined if any segment is missing. |
| SFDX_PROJECT_FILE_NAME | 'sfdx-project.json'. |
| SfdxProject, SfdxPackageDirectory, SfdxPackageDirectoryDependency | Types for the parsed file. |
Package IDs
| Export | Description |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| isPackage2Id, isPackage2VersionId, isSubscriberPackageId, isSubscriberPackageVersionId | Prefix predicates for the four package ID kinds. |
| PACKAGE_PREFIX_PACKAGE2, PACKAGE_PREFIX_PACKAGE2_VERSION, PACKAGE_PREFIX_SUBSCRIBER_PACKAGE, PACKAGE_PREFIX_SUBSCRIBER_PACKAGE_VERSION | The prefixes those predicates check (0Ho, 05i, 033, 04t). |
Metadata package attribution
| Export | Description |
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| resolvePackageNamesByApiName(conn, apiNames, options?) | Maps metadata API names to the installed package that owns them. |
| resolvePackageNamesBySubjectId(conn, subjectIds, options?) | The same, keyed by subject ID. |
| normalizePublisherName(publisherName, fallbackLabel) | Normalizes a publisher name for display, falling back to fallbackLabel when absent. |
| LOCAL_PACKAGE_LABEL, LOCAL_PUBLISHER_NAME | Labels used for metadata that belongs to the org itself rather than an installed package. |
| ResolvePackageNamesByApiNameOptions, ResolvePackageNamesOptions | Option types. |
Config files
| Export | Description |
| ----------------------------------------------------- | ----------------------------------------------------------- |
| loadJsonConfig(path, schema) / loadJsonConfigSync | Reads a JSON config file and validates it against a schema. |
| parseJsonConfig(contents, schema) | The same, from an already-read string. |
| ConfigSchema, JsonConfigResult | Schema and result types. |
Collections and paths
| Export | Description |
| --------------------------------- | ---------------------------------------------------------------------------------------------- |
| chunk(items, size) | Splits an array into fixed-size chunks. |
| mapChunked(items, size, mapper) | Maps an async mapper over an array one chunk at a time, awaiting each chunk before the next. |
| ensureDirectory(path) | Creates a directory (and parents) if it doesn't already exist. |
| timestampForFileName(date?) | A filesystem-safe timestamp, for generated output file names. |
Issues
Please report any issues at https://github.com/SimplySF/simply-node/issues
Contributing
This package is part of the @simplysf/simply monorepo. See CONTRIBUTING.md for what's specific to this package, and the repo's root CONTRIBUTING.md for repo structure, setup, commit conventions, and how to submit a pull request. Please also read our Code of Conduct.
License
Licensed under the Apache-2.0 license.
