UNPKG

@salesforce/packaging

Version:

Packaging library for the Salesforce packaging platform

441 lines 18.3 kB
"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