@salesforce/packaging
Version:
Packaging library for the Salesforce packaging platform
441 lines • 18.3 kB
JavaScript
"use strict";
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubscriberPackageVersion = exports.SubscriberPackageVersionFields = void 0;
const core_1 = require("@salesforce/core");
const kit_1 = require("@salesforce/kit");
const packageUtils_1 = require("../utils/packageUtils");
const packageInstall_1 = require("./packageInstall");
const packageUninstall_1 = require("./packageUninstall");
const packageVersionCreate_1 = require("./packageVersionCreate");
const versionNumber_1 = require("./versionNumber");
core_1.Messages.importMessagesDirectory(__dirname);
const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'subscriber_package_version');
const pkgMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'package');
// these fields have been identified as requiring additional serverside resources in order to calculate their values
// and are therefore not returned by default
// these will require additional queries to retrieve
const highCostQueryFields = [
'AppExchangeDescription',
'AppExchangeLogoUrl',
'AppExchangePackageName',
'AppExchangePublisherName',
'CspTrustedSite',
'Dependencies',
'PostInstallUrl',
'ReleaseNotesUrl',
'RemoteSiteSettings',
// 'InstallValidationStatus', // This requires extra resources on the server, but is commonly used, so let it load as part of the default query
'Profiles',
];
exports.SubscriberPackageVersionFields = [
'AppExchangeDescription',
'AppExchangeLogoUrl',
'AppExchangePackageName',
'AppExchangePublisherName',
'BuildNumber',
'CspTrustedSites',
'Dependencies',
'Description',
'Id',
'InstallValidationStatus',
'IsBeta',
'IsDeprecated',
'IsManaged',
'IsOrgDependent',
'IsPasswordProtected',
'IsSecurityReviewed',
'MajorVersion',
'MinorVersion',
'Name',
'Package2ContainerOptions',
'PatchVersion',
'PostInstallUrl',
'Profiles',
'PublisherName',
'ReleaseNotesUrl',
'ReleaseState',
'RemoteSiteSettings',
'SubscriberPackageId',
];
let logger;
const getLogger = () => {
if (!logger) {
logger = core_1.Logger.childFromRoot('subscriberPackageVersion');
}
return logger;
};
const allZeroesInstallOptions = {
pollingFrequency: kit_1.Duration.minutes(0),
pollingTimeout: kit_1.Duration.minutes(0),
publishFrequency: kit_1.Duration.minutes(0),
publishTimeout: kit_1.Duration.minutes(0),
};
/**
* Provides the ability to get, list, install, and uninstall 2nd
* generation subscriber package versions.
*
* **Examples**
*
* List all 2GP installed packages in the org:
*
* `const installedPkgs = await SubscriberPackageVersion.installedList(connection);`
*
* Install a 2GP subscriber package version:
*
* `const installStatus = await new SubscriberPackageVersion(options).install(request, options);`
*/
class SubscriberPackageVersion {
options;
password;
connection;
id;
data;
constructor(options) {
this.options = options;
this.connection = this.options.connection;
if (!this.options?.aliasOrId) {
throw messages.createError('errorInvalidAliasOrId', [this.options?.aliasOrId]);
}
try {
const project = core_1.SfProject.getInstance();
this.id = project.getPackageIdFromAlias(this.options.aliasOrId) ?? this.options.aliasOrId;
}
catch (error) {
const message = error instanceof Error ? error.message : error;
getLogger().debug(message);
this.id = this.options.aliasOrId;
}
// validate ID
if (!this.id.startsWith('04t') || !(0, core_1.validateSalesforceId)(this.id)) {
throw messages.createError('errorInvalidAliasOrId', [this.options.aliasOrId]);
}
this.password = this.options.password;
}
/**
* Fetches the status of a package version install request and will wait for the install to complete, if requested
* Package Version install emits the following events:
* - PackageEvents.install['subscriber-status']
*
* @param connection
* @param packageInstallRequestOrId
* @param installationKey
* @param options
*/
static async installStatus(connection, packageInstallRequestOrId, installationKey, options) {
const id = typeof packageInstallRequestOrId === 'string' ? packageInstallRequestOrId : packageInstallRequestOrId.Id;
const packageInstallRequest = await (0, packageInstall_1.getStatus)(connection, id);
const pollingTimeout = (0, packageUtils_1.numberToDuration)(options?.pollingTimeout);
if (pollingTimeout.milliseconds <= 0) {
return packageInstallRequest;
}
else {
await (0, packageInstall_1.waitForPublish)(connection, packageInstallRequest.SubscriberPackageVersionKey, options?.publishFrequency ?? 0, options?.publishTimeout ?? 0, installationKey);
return (0, packageInstall_1.pollStatus)(connection, id, options);
}
}
/**
* list the packages installed in the org
*
* @param conn: Connection to the org
*/
static async installedList(conn) {
try {
const query = 'SELECT Id, SubscriberPackageId, SubscriberPackage.NamespacePrefix, SubscriberPackage.Name, SubscriberPackageVersion.Id, SubscriberPackageVersion.Name, SubscriberPackageVersion.MajorVersion, SubscriberPackageVersion.MinorVersion, SubscriberPackageVersion.PatchVersion, SubscriberPackageVersion.BuildNumber, SubscriberPackageVersion.IsManaged, SubscriberPackageVersion.Package2ContainerOptions FROM InstalledSubscriberPackage ORDER BY SubscriberPackageId';
return (await conn.tooling.query(query)).records;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
/**
* Reports on the progress of a package version uninstall.
*
* @param id the 06y package version uninstall request id
* @param connection
*/
static async uninstallStatus(id, connection) {
if (!id.startsWith('06y') || !(0, core_1.validateSalesforceId)(id)) {
throw messages.createError('packageVersionUninstallRequestIdInvalid', [id]);
}
const result = (await connection.tooling.retrieve('SubscriberPackageVersionUninstallRequest', id));
if (result.Status === 'Error') {
const errorDetails = await (0, packageUninstall_1.getUninstallErrors)(connection, id);
const errors = errorDetails.map((record, index) => `(${index + 1}) ${record.Message}`);
const errHeader = errors.length > 0 ? `\n=== Errors\n${errors.join('\n')}` : '';
const err = pkgMessages.getMessage('defaultErrorMessage', [id, result.Id]);
throw new core_1.SfError(`${err}${errHeader}`, 'UNINSTALL_ERROR', [pkgMessages.getMessage('action')]);
}
return result;
}
/**
* Retrieves the package version create request.
*
* @param installRequestId
* @param connection
*/
static async getInstallRequest(installRequestId, connection) {
if (!installRequestId.startsWith('0Hf') || !(0, core_1.validateSalesforceId)(installRequestId)) {
throw messages.createError('packageVersionInstallRequestIdInvalid', [installRequestId]);
}
const installRequest = await (0, packageInstall_1.getStatus)(connection, installRequestId);
if (!installRequest) {
throw messages.createError('packageVersionInstallRequestNotFound', [installRequestId]);
}
return installRequest;
}
/**
* Resolve fields from a packageDirectories entry to a SubscriberPackageVersionId (04t).
* Specifically uses the `versionNumber` and `packageId` fields, as well as an optional
* `branch` field.
*
* @param connection A connection object to the org
* @param pkgDescriptor Fields from a packageDirectories entry in sfdx-project.json.
* The `versionNumber` and `packageId` fields are required. Optionally, the `branch` and
* `package` fields can be passed.
* @returns the SubscriberPackageVersionId (04t)
*/
static async resolveId(connection, pkgDescriptor) {
const pvc = new packageVersionCreate_1.PackageVersionCreate({ connection, project: core_1.SfProject.getInstance() });
return pvc.retrieveSubscriberPackageVersionId(pkgDescriptor);
}
/**
* Get the package version ID for this SubscriberPackageVersion.
*
* @returns The SubscriberPackageVersion Id (04t).
*/
getId() {
return Promise.resolve(this.id);
}
/**
* Get the package type for this SubscriberPackageVersion.
*
* @returns {PackageType} The package type ("Managed" or "Unlocked") for this SubscriberPackageVersion.
*/
async getPackageType() {
return this.getField('Package2ContainerOptions');
}
/**
* Get the password passed in the constructor
*
* @returns {string} the password
*/
getPassword() {
return this.password;
}
/**
* Get the subscriber package Id (033) for this SubscriberPackageVersion.
*
* @returns {string} The subscriber package Id (033).
*/
async getSubscriberPackageId() {
return this.getField('SubscriberPackageId');
}
/**
* Get a VersionNumber instance for this SubscriberPackageVersion.
*
* @returns {VersionNumber} The version number.
*/
async getVersionNumber() {
const majorVersion = await this.getField('MajorVersion');
const minorVersion = await this.getField('MinorVersion');
const patchVersion = await this.getField('PatchVersion');
const buildNumber = await this.getField('BuildNumber');
return new versionNumber_1.VersionNumber(majorVersion, minorVersion, patchVersion, buildNumber);
}
/**
* Is the package a managed package?
*/
async isManaged() {
return this.getField('IsManaged');
}
/**
* Is the SubscriberPackageVersion deprecated?
*
* @returns {boolean} True if the SubscriberPackageVersion is deprecated.
*/
async isDeprecated() {
return this.getField('IsDeprecated');
}
/**
* Is the SubscriberPackageVersion password protected?
*
* @returns {boolean} True if the SubscriberPackageVersion is password protected.
*/
async isPasswordProtected() {
return this.getField('IsPasswordProtected');
}
/**
* Is the SubscriberPackageVersion org dependent?
*
* @returns {boolean} True if the SubscriberPackageVersion is org dependent.
*/
async isOrgDependent() {
return this.getField('IsOrgDependent');
}
/**
* Return remote site settings for the SubscriberPackageVersion.
*
* @returns {RemoteSiteSettings} The remote site settings.
*/
async getRemoteSiteSettings() {
return this.getField('RemoteSiteSettings');
}
/**
* Return CSP trusted sites for the SubscriberPackageVersion.
*
* @returns {CspTrustedSites} The CSP trusted sites.
*/
async getCspTrustedSites() {
return this.getField('CspTrustedSites');
}
/**
* Get the installation validation status for the SubscriberPackageVersion.
*
* @returns {InstallationValidationStatus} The installation validation status.
*/
async getInstallValidationStatus() {
return this.getField('InstallValidationStatus');
}
/**
* Get the SubscriberPackageVersion SObject data for this SubscriberPackageVersion.
*
* @param force - force a refresh of the subscriber package version data.
* @returns {PackagingSObjects.SubscriberPackageVersion} SObject data.
*/
async getData(options = { force: false, includeHighCostFields: false }) {
if (!this.data || Boolean(options.force) || options.includeHighCostFields) {
const queryFields = this.getFieldsForQuery(options);
if (queryFields.length === 0) {
return this.data;
}
try {
let query = `SELECT ${queryFields.toString()} FROM SubscriberPackageVersion WHERE Id ='${await this.getId()}'`;
if (this.password) {
query = `${query} AND InstallationKey ='${(0, packageUtils_1.escapeInstallationKey)(this.password)}'`;
}
this.data = await this.connection.singleRecordQuery(query, { tooling: true });
}
catch (err) {
if (err instanceof Error) {
if (err.message === 'Request failed') {
// Use a better error message. This is typically a bad ID.
const errMsg = messages.getMessage('errorInvalidIdNoRecordFound', [this.options.aliasOrId]);
err.message = `${errMsg} - (${err.message})`;
}
throw core_1.SfError.create({
name: err.name,
message: err.message,
cause: err,
});
}
else {
throw core_1.SfError.wrap(err);
}
}
}
return this.data;
}
/**
* Wait for the subscriber package version to be replicated across instances and available to be queried against
*
* @param options.publishFrequency - how often to check for the package version to be published
* @param options.publishTimeout - how long to wait for the package version to be published
* @param options.installationKey - the installation key for the package version
*/
async waitForPublish(options = { publishFrequency: kit_1.Duration.seconds(5), publishTimeout: kit_1.Duration.minutes(5) }) {
await (0, packageInstall_1.waitForPublish)(this.connection, await this.getId(), options.publishFrequency, options.publishTimeout, options.installationKey);
}
/**
* Installs a package version in a subscriber org.
*
* Package Version install emits the following events:
* - PackageEvents.install.warning
* - PackageEvents.install.presend
* - PackageEvents.install.postsend
* - PackageEvents.install['subscriber-status']
*
* @param pkgInstallCreateRequest
* @param options
*/
async install(pkgInstallCreateRequest, options = allZeroesInstallOptions) {
try {
// before starting the install, check to see if the package version is available for install
await (0, packageInstall_1.waitForPublish)(this.connection, await this.getId(), options.publishFrequency, options.publishTimeout, pkgInstallCreateRequest.Password);
const pkgVersionInstallRequest = await (0, packageInstall_1.createPackageInstallRequest)(this.connection, pkgInstallCreateRequest, await this.getPackageType());
return await SubscriberPackageVersion.installStatus(this.connection, pkgVersionInstallRequest.Id, pkgInstallCreateRequest.Password, options);
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
/**
* Uninstalls a package version from a subscriber org.
*
* @param frequency
* @param wait
*/
async uninstall(frequency = kit_1.Duration.milliseconds(0), wait = kit_1.Duration.milliseconds(0)) {
return (0, packageUninstall_1.uninstallPackage)(await this.getId(), this.connection, frequency, wait);
}
/**
* Returns an array of RSS and CSP external sites for the package.
*
* @param installationKey The installation key (if any) for the subscriber package version.
* @returns an array of RSS and CSP site URLs, or undefined if the package doesn't have any.
*/
async getExternalSites() {
getLogger().debug(`Checking package: [${await this.getId()}] for external sites`);
const remoteSiteSettings = await this.getRemoteSiteSettings();
const cspTrustedSites = await this.getCspTrustedSites();
const rssUrls = remoteSiteSettings?.settings ? remoteSiteSettings.settings?.map((rss) => rss.url) : [];
const cspUrls = cspTrustedSites?.settings ? cspTrustedSites?.settings.map((csp) => csp.endpointUrl) : [];
const sites = [...rssUrls, ...cspUrls];
return sites.length > 0 ? sites : undefined;
}
/**
* Return dependencies for the SubscriberPackageVersion.
*
* @returns {Dependencies} The dependencies.
*/
async getDependencies() {
return this.getField('Dependencies');
}
/**
* Return a field value from the SubscriberPackageVersion SObject using the field name.
*
* @param field
*/
async getField(field) {
if (!this.data || !Reflect.has(this.data, field)) {
await this.getData({ includeHighCostFields: highCostQueryFields.includes(field) });
}
return Reflect.get(this.data ?? {}, field);
}
// eslint-disable-next-line class-methods-use-this
getFieldsForQuery(options) {
return exports.SubscriberPackageVersionFields.filter((field) => !highCostQueryFields.includes(field) || options.includeHighCostFields);
}
}
exports.SubscriberPackageVersion = SubscriberPackageVersion;
//# sourceMappingURL=subscriberPackageVersion.js.map