@salesforce/packaging
Version:
Packaging library for the Salesforce packaging platform
269 lines • 16.9 kB
JavaScript
;
/*
* 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.PackageBundleVersion = void 0;
const core_1 = require("@salesforce/core");
const kit_1 = require("@salesforce/kit");
const interfaces_1 = require("../interfaces");
const packageUtils_1 = require("../utils/packageUtils");
const bundleUtils_1 = require("../utils/bundleUtils");
const packageBundleVersionCreate_1 = require("./packageBundleVersionCreate");
core_1.Messages.importMessagesDirectory(__dirname);
const bundleVersionMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'bundle_version');
class PackageBundleVersion {
static async create(options) {
const createResult = await packageBundleVersionCreate_1.PackageBundleVersionCreate.createBundleVersion(options.connection, options.project, options);
if (options.polling) {
const finalResult = await PackageBundleVersion.pollCreateStatus(createResult.Id, options.connection, options.project, options.polling).catch((error) => {
if (error.name === 'PollingClientTimeout') {
const modifiedError = new core_1.SfError(error.message);
modifiedError.setData({ VersionCreateRequestId: createResult.Id });
modifiedError.message += ` Run 'sf package bundle version create report -i ${createResult.Id}' to check the status.`;
throw (0, packageUtils_1.applyErrorAction)((0, bundleUtils_1.massageErrorMessage)(modifiedError));
}
throw (0, packageUtils_1.applyErrorAction)((0, bundleUtils_1.massageErrorMessage)(error));
});
// Add bundle version alias to sfdx-project.json after successful creation
if (finalResult.RequestStatus === interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.success &&
finalResult.PackageBundleVersionId) {
await PackageBundleVersion.addBundleVersionAlias(options.project, finalResult);
}
return finalResult;
}
// Add bundle version alias to sfdx-project.json after successful creation (non-polling case)
// Note: In the non-polling case, the bundle version may not be created yet (status is 'Queued' or 'InProgress')
// So we only add the alias if the status is already 'Success'
if (createResult.RequestStatus === interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.success &&
createResult.PackageBundleVersionId) {
await PackageBundleVersion.addBundleVersionAlias(options.project, createResult);
}
return createResult;
}
static async pollCreateStatus(createPackageVersionRequestId, connection, project, polling) {
if (polling.timeout?.milliseconds <= 0) {
return packageBundleVersionCreate_1.PackageBundleVersionCreate.getCreateStatus(createPackageVersionRequestId, connection);
}
let remainingWaitTime = polling.timeout;
const pollingClient = await core_1.PollingClient.create({
poll: async () => {
const report = await packageBundleVersionCreate_1.PackageBundleVersionCreate.getCreateStatus(createPackageVersionRequestId, connection);
switch (report.RequestStatus) {
case interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.queued:
case interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.inProgress:
await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageVersionEvents.create.progress, { ...report, remainingWaitTime });
remainingWaitTime = kit_1.Duration.seconds(remainingWaitTime.seconds - polling.frequency.seconds);
return {
completed: false,
payload: report,
};
case interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.success: {
await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageVersionEvents.create.success, report);
return { completed: true, payload: report };
}
case interfaces_1.BundleSObjects.PkgBundleVersionCreateReqStatus.error:
await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageVersionEvents.create.error, report);
return { completed: true, payload: report };
default:
// Handle any unexpected status by continuing to poll
await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageVersionEvents.create.progress, { ...report, remainingWaitTime });
remainingWaitTime = kit_1.Duration.seconds(remainingWaitTime.seconds - polling.frequency.seconds);
return {
completed: false,
payload: report,
};
}
},
frequency: polling.frequency,
timeout: polling.timeout,
});
try {
return await pollingClient.subscribe();
}
catch (err) {
const report = await packageBundleVersionCreate_1.PackageBundleVersionCreate.getCreateStatus(createPackageVersionRequestId, connection);
await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageVersionEvents.create['timed-out'], report);
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)(err);
}
throw err;
}
}
static async report(connection, id) {
const query = `SELECT Id, PackageBundle.Id, PackageBundle.BundleName, VersionName, MajorVersion, MinorVersion, IsReleased, PackageBundle.Description, PackageBundle.IsDeleted, PackageBundle.CreatedDate, PackageBundle.CreatedById, PackageBundle.LastModifiedDate, PackageBundle.LastModifiedById, PackageBundle.SystemModstamp, Ancestor.Id, Ancestor.PackageBundle.Id, Ancestor.PackageBundle.BundleName, Ancestor.VersionName, Ancestor.MajorVersion, Ancestor.MinorVersion, Ancestor.IsReleased, Ancestor.PackageBundle.Description, Ancestor.PackageBundle.IsDeleted, Ancestor.PackageBundle.CreatedDate, Ancestor.PackageBundle.CreatedById, Ancestor.PackageBundle.LastModifiedDate, Ancestor.PackageBundle.LastModifiedById, Ancestor.PackageBundle.SystemModstamp FROM PackageBundleVersion WHERE Id = '${id}'`;
const queryResult = await connection.autoFetchQuery(query, { tooling: true });
return queryResult.records.length > 0
? PackageBundleVersion.mapRecordToBundleVersion(queryResult.records[0])
: null;
}
static async list(connection) {
const query = 'SELECT Id, PackageBundle.Id, PackageBundle.BundleName, VersionName, MajorVersion, MinorVersion, IsReleased, PackageBundle.Description, PackageBundle.IsDeleted, PackageBundle.CreatedDate, PackageBundle.CreatedById, PackageBundle.LastModifiedDate, PackageBundle.LastModifiedById, PackageBundle.SystemModstamp, Ancestor.Id, Ancestor.PackageBundle.Id, Ancestor.PackageBundle.BundleName, Ancestor.VersionName, Ancestor.MajorVersion, Ancestor.MinorVersion, Ancestor.IsReleased, Ancestor.PackageBundle.Description, Ancestor.PackageBundle.IsDeleted, Ancestor.PackageBundle.CreatedDate, Ancestor.PackageBundle.CreatedById, Ancestor.PackageBundle.LastModifiedDate, Ancestor.PackageBundle.LastModifiedById, Ancestor.PackageBundle.SystemModstamp FROM PackageBundleVersion';
const queryResult = await connection.autoFetchQuery(query, { tooling: true });
return queryResult.records.map((record) => PackageBundleVersion.mapRecordToBundleVersion(record));
}
static async getComponentPackages(connection, id) {
const query = `SELECT Component.Id, Component.Description, Component.PublisherName, Component.MajorVersion, Component.MinorVersion, Component.PatchVersion, Component.BuildNumber, Component.ReleaseState, Component.IsManaged, Component.IsDeprecated, Component.IsPasswordProtected, Component.IsBeta, Component.Package2ContainerOptions, Component.IsSecurityReviewed, Component.IsOrgDependent, Component.AppExchangePackageName, Component.AppExchangeDescription, Component.AppExchangePublisherName, Component.AppExchangeLogoUrl, Component.ReleaseNotesUrl, Component.PostInstallUrl, Component.RemoteSiteSettings, Component.CspTrustedSites, Component.Profiles, Component.Dependencies, Component.InstallValidationStatus, Component.SubscriberPackageId FROM PkgBundleVersionComponent WHERE PackageBundleVersion.Id = '${id}' ORDER BY CreatedDate`;
const queryResult = await connection.autoFetchQuery(query, { tooling: true });
// Get unique SubscriberPackageIds to query for Names
const subscriberPackageIds = [
...new Set(queryResult.records
.map((record) => record.Component?.SubscriberPackageId)
.filter((packageId) => !!packageId)),
];
// Query SubscriberPackage to get Names (one by one due to implementation restriction)
const subscriberPackageNames = new Map();
const packageQueries = subscriberPackageIds.map(async (packageId) => {
try {
const packageQuery = `SELECT Id, Name FROM SubscriberPackage WHERE Id='${packageId}'`;
const packageQueryResult = await connection.autoFetchQuery(packageQuery, { tooling: true });
return {
packageId,
name: packageQueryResult.records.length > 0 ? packageQueryResult.records[0].Name : '',
};
}
catch (error) {
// If individual query fails, return empty name for this package
return {
packageId,
name: '',
};
}
});
const packageResults = await Promise.allSettled(packageQueries);
packageResults.forEach((result) => {
if (result.status === 'fulfilled') {
subscriberPackageNames.set(result.value.packageId, result.value.name);
}
});
return queryResult.records.map((record) => {
const component = record.Component;
if (!component) {
throw new core_1.SfError(bundleVersionMessages.getMessage('componentRecordMissing'));
}
const packageName = subscriberPackageNames.get(component.SubscriberPackageId) ?? '';
return {
Id: component.Id,
SubscriberPackageId: component.SubscriberPackageId,
Name: packageName,
Description: component.Description,
PublisherName: component.PublisherName,
MajorVersion: component.MajorVersion,
MinorVersion: component.MinorVersion,
PatchVersion: component.PatchVersion,
BuildNumber: component.BuildNumber,
ReleaseState: component.ReleaseState,
IsManaged: component.IsManaged,
IsDeprecated: component.IsDeprecated,
IsPasswordProtected: component.IsPasswordProtected,
IsBeta: component.IsBeta,
Package2ContainerOptions: component.Package2ContainerOptions,
IsSecurityReviewed: component.IsSecurityReviewed,
IsOrgDependent: component.IsOrgDependent,
AppExchangePackageName: component.AppExchangePackageName,
AppExchangeDescription: component.AppExchangeDescription,
AppExchangePublisherName: component.AppExchangePublisherName,
AppExchangeLogoUrl: component.AppExchangeLogoUrl,
ReleaseNotesUrl: component.ReleaseNotesUrl,
PostInstallUrl: component.PostInstallUrl,
RemoteSiteSettings: component.RemoteSiteSettings,
CspTrustedSites: component.CspTrustedSites,
Profiles: component.Profiles,
Dependencies: component.Dependencies,
InstallValidationStatus: component.InstallValidationStatus,
};
});
}
static mapRecordToBundleVersion(record) {
return {
Id: record.Id,
PackageBundle: PackageBundleVersion.mapPackageBundle(record.PackageBundle),
VersionName: record.VersionName ?? '',
MajorVersion: record.MajorVersion ?? '',
MinorVersion: record.MinorVersion ?? '',
CreatedDate: record.PackageBundle?.CreatedDate ?? '',
CreatedById: record.PackageBundle?.CreatedById ?? '',
LastModifiedDate: record.PackageBundle?.LastModifiedDate ?? '',
LastModifiedById: record.PackageBundle?.LastModifiedById ?? '',
Ancestor: record.Ancestor?.Id ? PackageBundleVersion.mapAncestor(record.Ancestor) : null,
IsReleased: record.IsReleased ?? false,
};
}
static mapPackageBundle(packageBundle) {
return {
Id: packageBundle?.Id ?? '',
BundleName: packageBundle?.BundleName ?? '',
Description: packageBundle?.Description,
IsDeleted: packageBundle?.IsDeleted ?? false,
CreatedDate: packageBundle?.CreatedDate ?? '',
CreatedById: packageBundle?.CreatedById ?? '',
LastModifiedDate: packageBundle?.LastModifiedDate ?? '',
LastModifiedById: packageBundle?.LastModifiedById ?? '',
SystemModstamp: packageBundle?.SystemModstamp ?? '',
};
}
static mapAncestor(ancestor) {
return {
Id: ancestor.Id,
PackageBundle: PackageBundleVersion.mapAncestorPackageBundle(ancestor.PackageBundle),
VersionName: ancestor?.VersionName ?? '',
MajorVersion: ancestor?.MajorVersion ?? '',
MinorVersion: ancestor?.MinorVersion ?? '',
CreatedDate: ancestor.PackageBundle?.CreatedDate ?? '',
CreatedById: ancestor.PackageBundle?.CreatedById ?? '',
LastModifiedDate: ancestor.PackageBundle?.LastModifiedDate ?? '',
LastModifiedById: ancestor.PackageBundle?.LastModifiedById ?? '',
Ancestor: null,
IsReleased: ancestor.IsReleased ?? false,
};
}
static mapAncestorPackageBundle(packageBundle) {
return {
Id: packageBundle?.Id ?? '',
BundleName: packageBundle?.BundleName ?? '',
Description: packageBundle?.Description,
IsDeleted: packageBundle?.IsDeleted ?? false,
CreatedDate: packageBundle?.CreatedDate ?? '',
CreatedById: packageBundle?.CreatedById ?? '',
LastModifiedDate: packageBundle?.LastModifiedDate ?? '',
LastModifiedById: packageBundle?.LastModifiedById ?? '',
SystemModstamp: packageBundle?.SystemModstamp ?? '',
};
}
/**
* Add a bundle version alias to the sfdx-project.json file after successful bundle version creation.
* Creates an alias in the format: <BundleName>@<MajorVersion>.<MinorVersion>
*
* @param project The SfProject instance
* @param result The bundle version create result containing bundle information
*/
static async addBundleVersionAlias(project, result) {
// Skip if auto-update is disabled
if (kit_1.env.getBoolean('SF_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_CREATE')) {
return;
}
// Ensure we have the necessary information to create the alias
if (!result.PackageBundleVersionId || !result.VersionName || !result.MajorVersion || !result.MinorVersion) {
return;
}
// Create alias in format: BundleName@MajorVersion.MinorVersion
const alias = `${result.VersionName}@${result.MajorVersion}.${result.MinorVersion}`;
// Add the alias to the sfdx-project.json file
project.getSfProjectJson().addPackageBundleAlias(alias, result.PackageBundleVersionId);
await project.getSfProjectJson().write();
}
}
exports.PackageBundleVersion = PackageBundleVersion;
//# sourceMappingURL=packageBundleVersion.js.map