@salesforce/packaging 1.4.16 → 1.4.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/interfaces/packagingInterfacesAndType.d.ts +37 -33
  2. package/lib/package/package.d.ts +3 -3
  3. package/lib/package/package.js +14 -8
  4. package/lib/package/packageAncestry.d.ts +24 -25
  5. package/lib/package/packageAncestry.js +159 -147
  6. package/lib/package/packageConvert.d.ts +1 -1
  7. package/lib/package/packageConvert.js +15 -21
  8. package/lib/package/packageCreate.d.ts +1 -1
  9. package/lib/package/packageCreate.js +14 -25
  10. package/lib/package/packageDelete.js +4 -1
  11. package/lib/package/packageInstall.d.ts +5 -4
  12. package/lib/package/packageInstall.js +10 -9
  13. package/lib/package/packageProfileApi.d.ts +3 -6
  14. package/lib/package/packageProfileApi.js +153 -145
  15. package/lib/package/packageUninstall.js +15 -9
  16. package/lib/package/packageVersion.d.ts +6 -5
  17. package/lib/package/packageVersion.js +76 -46
  18. package/lib/package/packageVersionCreate.d.ts +2 -2
  19. package/lib/package/packageVersionCreate.js +81 -59
  20. package/lib/package/packageVersionCreateRequest.d.ts +2 -2
  21. package/lib/package/packageVersionCreateRequest.js +8 -11
  22. package/lib/package/packageVersionCreateRequestReport.js +4 -1
  23. package/lib/package/packageVersionList.d.ts +5 -4
  24. package/lib/package/packageVersionList.js +18 -18
  25. package/lib/package/packageVersionReport.js +6 -4
  26. package/lib/package/subscriberPackageVersion.d.ts +4 -4
  27. package/lib/package/subscriberPackageVersion.js +23 -10
  28. package/lib/package/versionNumber.d.ts +8 -3
  29. package/lib/package/versionNumber.js +13 -4
  30. package/lib/package1/package1Version.js +18 -13
  31. package/lib/utils/packageUtils.d.ts +11 -7
  32. package/lib/utils/packageUtils.js +25 -12
  33. package/messages/package.md +4 -0
  34. package/messages/package1Version.md +4 -0
  35. package/messages/package_version.md +8 -0
  36. package/messages/package_version_create.md +32 -4
  37. package/messages/pkg_utils.md +1 -1
  38. package/package.json +13 -12
@@ -8,13 +8,10 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.createPackage = exports.createPackageDirEntry = exports.createPackageRequestFromContext = void 0;
10
10
  const core_1 = require("@salesforce/core");
11
- const ts_types_1 = require("@salesforce/ts-types");
12
11
  const pkgUtils = require("../utils/packageUtils");
13
12
  const packageUtils_1 = require("../utils/packageUtils");
14
- core_1.Messages.importMessagesDirectory(__dirname);
15
- const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_create');
16
13
  function createPackageRequestFromContext(project, options) {
17
- const namespace = options.noNamespace ? '' : project.getSfProjectJson().getContents().namespace || '';
14
+ const namespace = options.noNamespace ? '' : project.getSfProjectJson().getContents().namespace ?? '';
18
15
  return {
19
16
  Name: options.name,
20
17
  Description: options.description,
@@ -29,22 +26,21 @@ exports.createPackageRequestFromContext = createPackageRequestFromContext;
29
26
  * Create packageDirectory json entry for this package that can be written to sfdx-project.json
30
27
  *
31
28
  * @param project
32
- * @param packageId the 0Ho id of the package to create the entry for
29
+ * @param options - package create options
33
30
  * @private
34
31
  */
35
32
  function createPackageDirEntry(project, options) {
36
- let packageDirs = project.getSfProjectJson().getContents().packageDirectories;
33
+ const packageDirs = project.getSfProjectJson().getContents().packageDirectories ?? [];
37
34
  let isNew = false;
38
- if (!packageDirs) {
39
- packageDirs = [];
40
- }
41
35
  // see if package exists (exists means it has an id or package)
42
- let packageDir = packageDirs.find((pd) => pd.path === options.path && !pd.id && !pd.package);
36
+ let packageDir = packageDirs
37
+ .map((pd) => pd)
38
+ .find((pd) => pd.path === options.path && !pd.id && !pd.package);
43
39
  if (!packageDir) {
44
40
  // no match - create a new one
45
41
  isNew = true;
46
42
  packageDir = pkgUtils.DEFAULT_PACKAGE_DIR;
47
- packageDir.path = packageDir.path || options.path;
43
+ packageDir.path = (0, packageUtils_1.replaceIfEmpty)(packageDir.path, options.path);
48
44
  }
49
45
  if (packageDirs.length === 0) {
50
46
  packageDir.default = true;
@@ -52,10 +48,10 @@ function createPackageDirEntry(project, options) {
52
48
  else if (isNew) {
53
49
  packageDir.default = !packageDirs.find((pd) => pd.default);
54
50
  }
55
- packageDir.package = packageDir.package || options.name;
56
- packageDir.versionName = packageDir.versionName || pkgUtils.DEFAULT_PACKAGE_DIR.versionName;
57
- packageDir.versionNumber = packageDir.versionNumber || pkgUtils.DEFAULT_PACKAGE_DIR.versionNumber;
58
- packageDir.versionDescription = packageDir.versionDescription || options.description;
51
+ packageDir.package = (0, packageUtils_1.replaceIfEmpty)(packageDir.package, options.name);
52
+ packageDir.versionName = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionName, pkgUtils.DEFAULT_PACKAGE_DIR.versionName);
53
+ packageDir.versionNumber = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionNumber, pkgUtils.DEFAULT_PACKAGE_DIR.versionNumber);
54
+ packageDir.versionDescription = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionDescription, options.description);
59
55
  return packageDir;
60
56
  }
61
57
  exports.createPackageDirEntry = createPackageDirEntry;
@@ -63,30 +59,23 @@ async function createPackage(connection, project, options) {
63
59
  // strip trailing slash from path param
64
60
  options.path = options.path.replace(/\/$/, '');
65
61
  const request = createPackageRequestFromContext(project, options);
66
- let packageId = null;
67
62
  const createResult = await connection.tooling
68
63
  .sobject('Package2')
69
64
  .create(request)
70
65
  .catch((err) => {
71
- const error = (0, ts_types_1.isString)(err) ? new Error(err) : err;
66
+ const error = err instanceof Error ? err : new Error(typeof err === 'string' ? err : 'Unknown error');
72
67
  throw core_1.SfError.wrap((0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(error)));
73
68
  });
74
69
  if (!createResult.success) {
75
70
  throw pkgUtils.combineSaveErrors('Package2', 'create', createResult.errors);
76
71
  }
77
- packageId = createResult.id;
78
- const queryResult = await connection.tooling.query(`SELECT Id FROM Package2 WHERE Id='${packageId}'`);
79
- if (!queryResult?.records[0]) {
80
- throw messages.createError('unableToFindPackageWithId', [packageId]);
81
- }
82
- const record = queryResult.records[0];
83
72
  if (!process.env.SFDX_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_CREATE) {
84
73
  const packageDirectory = createPackageDirEntry(project, options);
85
74
  project.getSfProjectJson().addPackageDirectory(packageDirectory);
86
- project.getSfProjectJson().addPackageAlias(options.name, record.Id);
75
+ project.getSfProjectJson().addPackageAlias(options.name, createResult.id);
87
76
  await project.getSfProjectJson().write();
88
77
  }
89
- return { Id: record.Id };
78
+ return { Id: createResult.id };
90
79
  }
91
80
  exports.createPackage = createPackage;
92
81
  //# sourceMappingURL=packageCreate.js.map
@@ -16,7 +16,10 @@ async function deletePackage(idOrAlias, project, connection, undelete) {
16
16
  const isUndelete = undelete;
17
17
  request.IsDeprecated = !isUndelete;
18
18
  const updateResult = await connection.tooling.update('Package2', request).catch((err) => {
19
- throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
19
+ if (err instanceof Error) {
20
+ throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
21
+ }
22
+ throw err;
20
23
  });
21
24
  if (!updateResult.success) {
22
25
  throw (0, packageUtils_1.combineSaveErrors)('Package2', 'update', updateResult.errors);
@@ -1,12 +1,13 @@
1
1
  import { Connection } from '@salesforce/core';
2
+ import { Nullable } from '@salesforce/ts-types';
2
3
  import { QueryResult } from 'jsforce';
3
4
  import { Duration } from '@salesforce/kit';
4
- import { PackagingSObjects, PackageInstallCreateRequest, PackageType, PackageInstallOptions } from '../interfaces';
5
+ import { PackageInstallCreateRequest, PackageInstallOptions, PackageType, PackagingSObjects } from '../interfaces';
5
6
  import PackageInstallRequest = PackagingSObjects.PackageInstallRequest;
6
7
  export declare function createPackageInstallRequest(connection: Connection, pkgInstallCreateRequest: PackageInstallCreateRequest, packageType: PackageType): Promise<PackagingSObjects.PackageInstallRequest>;
7
8
  export declare function getStatus(connection: Connection, packageInstallRequestId: string): Promise<PackageInstallRequest>;
8
9
  export declare function isErrorFromSPVQueryRestriction(err: Error): boolean;
9
10
  export declare function isErrorPackageNotAvailable(err: Error): boolean;
10
- export declare function getInstallationStatus(subscriberPackageVersionId: string, installationKey: string, connection: Connection): Promise<QueryResult<PackagingSObjects.SubscriberPackageVersion>>;
11
- export declare function waitForPublish(connection: Connection, subscriberPackageVersionId: string, frequency: number | Duration, timeout: number | Duration, installationKey?: string): Promise<void>;
12
- export declare function pollStatus(connection: Connection, installRequestId: string, options: PackageInstallOptions): Promise<PackageInstallRequest>;
11
+ export declare function getInstallationStatus(subscriberPackageVersionId: string, installationKey: Nullable<string>, connection: Connection): Promise<QueryResult<PackagingSObjects.SubscriberPackageVersion> | undefined>;
12
+ export declare function waitForPublish(connection: Connection, subscriberPackageVersionId: string, frequency?: number | Duration, timeout?: number | Duration, installationKey?: string | Nullable<string>): Promise<void>;
13
+ export declare function pollStatus(connection: Connection, installRequestId: string, options?: PackageInstallOptions): Promise<PackageInstallRequest>;
@@ -60,7 +60,8 @@ async function createPackageInstallRequest(connection, pkgInstallCreateRequest,
60
60
  }
61
61
  exports.createPackageInstallRequest = createPackageInstallRequest;
62
62
  async function getStatus(connection, packageInstallRequestId) {
63
- return (await connection.tooling.retrieve('PackageInstallRequest', packageInstallRequestId));
63
+ const results = (await connection.tooling.retrieve('PackageInstallRequest', packageInstallRequestId));
64
+ return results;
64
65
  }
65
66
  exports.getStatus = getStatus;
66
67
  // determines if error is from malformed SubscriberPackageVersion query
@@ -94,14 +95,14 @@ async function getInstallationStatus(subscriberPackageVersionId, installationKey
94
95
  }
95
96
  exports.getInstallationStatus = getInstallationStatus;
96
97
  async function waitForPublish(connection, subscriberPackageVersionId, frequency, timeout, installationKey) {
97
- const pollingTimeout = (0, packageUtils_1.numberToDuration)(timeout || 0);
98
+ const pollingTimeout = (0, packageUtils_1.numberToDuration)(timeout);
98
99
  if (pollingTimeout.milliseconds <= 0) {
99
100
  return;
100
101
  }
101
102
  let queryResult;
102
103
  let installValidationStatus = 'PACKAGE_UNAVAILABLE';
103
104
  const pollingOptions = {
104
- frequency: (0, packageUtils_1.numberToDuration)(frequency || 0),
105
+ frequency: (0, packageUtils_1.numberToDuration)(frequency),
105
106
  timeout: pollingTimeout,
106
107
  poll: async () => {
107
108
  queryResult = await getInstallationStatus(subscriberPackageVersionId, installationKey, connection);
@@ -130,7 +131,7 @@ async function waitForPublish(connection, subscriberPackageVersionId, frequency,
130
131
  // if polling timed out
131
132
  const error = installMsgs.createError('subscriberPackageVersionNotPublished');
132
133
  error.setData(queryResult?.records[0]);
133
- if (error.stack && e.stack) {
134
+ if (error.stack && e instanceof Error && e.stack) {
134
135
  getLogger().debug(`Error during waitForPublish polling:\n${e.stack}`);
135
136
  // append the original stack to this new error
136
137
  error.stack += `\nDUE TO:\n${e.stack}`;
@@ -139,11 +140,11 @@ async function waitForPublish(connection, subscriberPackageVersionId, frequency,
139
140
  }
140
141
  }
141
142
  exports.waitForPublish = waitForPublish;
142
- async function pollStatus(connection, installRequestId, options) {
143
- let packageInstallRequest;
143
+ async function pollStatus(connection, installRequestId, options = { pollingFrequency: 5000, pollingTimeout: 300000 }) {
144
+ let packageInstallRequest = await getStatus(connection, installRequestId);
144
145
  const { pollingFrequency, pollingTimeout } = options;
145
- const frequency = (0, packageUtils_1.numberToDuration)(pollingFrequency || 5000);
146
- const timeout = (0, packageUtils_1.numberToDuration)(pollingTimeout || 300000);
146
+ const frequency = (0, packageUtils_1.numberToDuration)(pollingFrequency);
147
+ const timeout = (0, packageUtils_1.numberToDuration)(pollingTimeout);
147
148
  const pollingOptions = {
148
149
  frequency,
149
150
  timeout,
@@ -169,7 +170,7 @@ async function pollStatus(connection, installRequestId, options) {
169
170
  const errMsg = e instanceof Error ? e.message : (0, ts_types_1.isString)(e) ? e : 'polling timed out';
170
171
  const error = new core_1.SfError(errMsg, 'PackageInstallTimeout');
171
172
  error.setData(packageInstallRequest);
172
- if (error.stack && e.stack) {
173
+ if (error.stack && e instanceof Error && e.stack) {
173
174
  // add the original stack to this new error
174
175
  error.stack += `\nDUE TO:\n${e.stack}`;
175
176
  }
@@ -1,21 +1,18 @@
1
- import { ConfigAggregator, SfProject } from '@salesforce/core';
1
+ import { SfProject } from '@salesforce/core';
2
2
  import { AsyncCreatable } from '@salesforce/kit';
3
3
  import { ProfileApiOptions } from '../interfaces';
4
4
  export declare class PackageProfileApi extends AsyncCreatable<ProfileApiOptions> {
5
- private options;
6
5
  readonly profiles: ProfileInformation[];
7
- apiVersion: string;
8
6
  nodeEntities: {
9
7
  name: string[];
10
- childElement: string[];
11
8
  parentElement: string[];
9
+ childElement: string[];
12
10
  };
13
11
  otherProfileSettings: {
14
12
  name: string[];
15
- childElement: string[];
16
13
  parentElement: string[];
14
+ childElement: string[];
17
15
  };
18
- config: ConfigAggregator;
19
16
  project: SfProject;
20
17
  includeUserLicenses: boolean;
21
18
  generateProfileInformation: boolean;
@@ -15,6 +15,63 @@ const core_1 = require("@salesforce/core");
15
15
  const kit_1 = require("@salesforce/kit");
16
16
  core_1.Messages.importMessagesDirectory(__dirname);
17
17
  const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'profile_api');
18
+ // nodeEntities is used to determine which elements in the profile are relevant to the source being packaged.
19
+ // name refers to the entity type name in source that the element pertains to. As an example, a profile may
20
+ // have an entry like the example below, which should only be added to the packaged profile if the related
21
+ // CustomObject is in the source being packaged:
22
+ // <objectPermissions>
23
+ // <allowCreate>true</allowCreate>
24
+ // ...
25
+ // <object>MyCustomObject__c</object>
26
+ // ...
27
+ // </objectPermissions>
28
+ //
29
+ // For this example: nodeEntities.parentElement = objectPermissions and nodeEntities.childElement = object
30
+ const NODE_ENTITIES = {
31
+ name: [
32
+ 'CustomObject',
33
+ 'CustomField',
34
+ 'Layout',
35
+ 'CustomTab',
36
+ 'CustomApplication',
37
+ 'ApexClass',
38
+ 'CustomPermission',
39
+ 'ApexPage',
40
+ 'ExternalDataSource',
41
+ 'RecordType',
42
+ ],
43
+ parentElement: [
44
+ 'objectPermissions',
45
+ 'fieldPermissions',
46
+ 'layoutAssignments',
47
+ 'tabVisibilities',
48
+ 'applicationVisibilities',
49
+ 'classAccesses',
50
+ 'customPermissions',
51
+ 'pageAccesses',
52
+ 'externalDataSourceAccesses',
53
+ 'recordTypeVisibilities',
54
+ ],
55
+ childElement: [
56
+ 'object',
57
+ 'field',
58
+ 'layout',
59
+ 'tab',
60
+ 'application',
61
+ 'apexClass',
62
+ 'name',
63
+ 'apexPage',
64
+ 'externalDataSource',
65
+ 'recordType',
66
+ ],
67
+ };
68
+ // There are some profile settings that are allowed to be packaged that may not necessarily map to a specific metadata
69
+ // object. We should still handle these accordingly, but a little differently than the above mentioned types.
70
+ const OTHER_PROFILE_SETTINGS = {
71
+ name: ['CustomSettings', 'CustomMetadataTypeAccess'],
72
+ parentElement: ['customSettingAccesses', 'customMetadataTypeAccesses'],
73
+ childElement: ['name', 'name'],
74
+ };
18
75
  /*
19
76
  * This class provides functions used to re-write .profiles in the workspace when creating a package2 version.
20
77
  * All profiles found in the workspaces are extracted out and then re-written to only include metadata in the profile
@@ -23,74 +80,17 @@ const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging',
23
80
  class PackageProfileApi extends kit_1.AsyncCreatable {
24
81
  constructor(options) {
25
82
  super(options);
26
- this.options = options;
27
83
  this.profiles = [];
84
+ this.nodeEntities = NODE_ENTITIES;
85
+ this.otherProfileSettings = OTHER_PROFILE_SETTINGS;
86
+ this.includeUserLicenses = false;
28
87
  this.generateProfileInformation = false;
88
+ this.project = options.project;
89
+ this.includeUserLicenses = options.includeUserLicenses ?? false;
90
+ this.generateProfileInformation = options.generateProfileInformation ?? false;
29
91
  }
30
- async init() {
31
- this.project = this.options.project;
32
- this.includeUserLicenses = this.options.includeUserLicenses;
33
- this.generateProfileInformation = this.options.generateProfileInformation;
34
- this.config = await core_1.ConfigAggregator.create();
35
- this.apiVersion = this.config.getPropertyValue('apiVersion');
36
- // nodeEntities is used to determine which elements in the profile are relevant to the source being packaged.
37
- // name refers to the entity type name in source that the element pertains to. As an example, a profile may
38
- // have an entry like the example below, which should only be added to the packaged profile if the related
39
- // CustomObject is in the source being packaged:
40
- // <objectPermissions>
41
- // <allowCreate>true</allowCreate>
42
- // ...
43
- // <object>MyCustomObject__c</object>
44
- // ...
45
- // </objectPermissions>
46
- //
47
- // For this example: nodeEntities.parentElement = objectPermissions and nodeEntities.childElement = object
48
- this.nodeEntities = {
49
- name: [
50
- 'CustomObject',
51
- 'CustomField',
52
- 'Layout',
53
- 'CustomTab',
54
- 'CustomApplication',
55
- 'ApexClass',
56
- 'CustomPermission',
57
- 'ApexPage',
58
- 'ExternalDataSource',
59
- 'RecordType',
60
- ],
61
- parentElement: [
62
- 'objectPermissions',
63
- 'fieldPermissions',
64
- 'layoutAssignments',
65
- 'tabVisibilities',
66
- 'applicationVisibilities',
67
- 'classAccesses',
68
- 'customPermissions',
69
- 'pageAccesses',
70
- 'externalDataSourceAccesses',
71
- 'recordTypeVisibilities',
72
- ],
73
- childElement: [
74
- 'object',
75
- 'field',
76
- 'layout',
77
- 'tab',
78
- 'application',
79
- 'apexClass',
80
- 'name',
81
- 'apexPage',
82
- 'externalDataSource',
83
- 'recordType',
84
- ],
85
- };
86
- // There are some profile settings that are allowed to be packaged that may not necessarily map to a specific metadata
87
- // object. We should still handle these accordingly, but a little differently than the above mentioned types.
88
- this.otherProfileSettings = {
89
- name: ['CustomSettings', 'CustomMetadataTypeAccess'],
90
- parentElement: ['customSettingAccesses', 'customMetadataTypeAccesses'],
91
- childElement: ['name', 'name'],
92
- };
93
- }
92
+ // eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-empty-function
93
+ async init() { }
94
94
  /**
95
95
  * For any profile present in the workspace, this function generates a subset of data that only contains references
96
96
  * to items in the manifest.
@@ -107,71 +107,74 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
107
107
  }
108
108
  profilePaths.forEach((profilePath) => {
109
109
  // profile metadata can present in any directory in the package structure
110
- const profileName = profilePath.match(/([^/]+)\.profile-meta.xml/)[1];
111
- const profileDom = new xmldom_1.DOMParser().parseFromString(fs.readFileSync(profilePath, 'utf-8'));
112
- const newDom = new xmldom_1.DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata"></Profile>');
113
- const profileNode = newDom.getElementsByTagName('Profile')[0];
114
- let hasNodes = false;
115
- // We need to keep track of all the members for when we package up the "OtherProfileSettings"
116
- let allMembers = [];
117
- manifest.Package.forEach((element) => {
118
- const name = element.name;
119
- const members = element.members;
120
- allMembers = allMembers.concat(members);
121
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
122
- const idx = this.nodeEntities.name.indexOf(name[0]);
123
- if (idx > -1) {
124
- hasNodes =
125
- this.copyNodes(profileDom, this.nodeEntities.parentElement[idx], this.nodeEntities.childElement[idx], members, profileNode, profileName) || hasNodes;
126
- }
127
- });
128
- // Go through each of the other profile settings we might want to include. We pass in "all" the members since these
129
- // will reference anything that could be packaged. The "copyNodes" function will only include the type if it
130
- // exists in the profile itself
131
- this.otherProfileSettings.name.forEach((element) => {
132
- const idx = this.otherProfileSettings.name.indexOf(element);
133
- if (idx > -1) {
134
- hasNodes =
135
- this.copyNodes(profileDom, this.otherProfileSettings.parentElement[idx], this.otherProfileSettings.childElement[idx], allMembers, profileNode, profileName) || hasNodes;
136
- }
137
- });
138
- // add userLicenses to the profile
139
- if (this.includeUserLicenses === true) {
140
- const userLicenses = profileDom.getElementsByTagName('userLicense');
141
- if (userLicenses) {
142
- hasNodes = true;
143
- for (const userLicense of Array.from(userLicenses)) {
144
- profileNode.appendChild(userLicense.cloneNode(true));
110
+ const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
111
+ const profileName = profileNameMatch ? profileNameMatch[1] : null;
112
+ if (profileName) {
113
+ const profileDom = new xmldom_1.DOMParser().parseFromString(fs.readFileSync(profilePath, 'utf-8'));
114
+ const newDom = new xmldom_1.DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata"></Profile>');
115
+ const profileNode = newDom.getElementsByTagName('Profile')[0];
116
+ let hasNodes = false;
117
+ // We need to keep track of all the members for when we package up the "OtherProfileSettings"
118
+ let allMembers = [];
119
+ manifest.Package.forEach((element) => {
120
+ const name = element.name;
121
+ const members = element.members;
122
+ allMembers = allMembers.concat(members);
123
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
124
+ const idx = this.nodeEntities.name.indexOf(name[0]);
125
+ if (idx > -1) {
126
+ hasNodes =
127
+ this.copyNodes(profileDom, this.nodeEntities.parentElement[idx], this.nodeEntities.childElement[idx], members, profileNode, profileName) ?? hasNodes;
145
128
  }
146
- }
147
- }
148
- const xmlSrcFile = path.basename(profilePath);
149
- const xmlFile = xmlSrcFile.replace(/(.*)(-meta.xml)/, '$1');
150
- const destFilePath = path.join(destPath, xmlFile);
151
- if (hasNodes) {
152
- const serializer = new xmldom_1.XMLSerializer();
153
- serializer.serializeToString(newDom);
154
- fs.writeFileSync(destFilePath, serializer.serializeToString(newDom), 'utf-8');
155
- }
156
- else {
157
- // remove from manifest
158
- // eslint-disable-next-line @typescript-eslint/no-shadow
159
- const profileName = xmlFile.replace(/(.*)(\.profile)/, '$1');
160
- excludedProfiles.push(profileName);
161
- if (this.generateProfileInformation) {
162
- const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
163
- if (profile) {
164
- profile.setIsPackaged(false);
129
+ });
130
+ // Go through each of the other profile settings we might want to include. We pass in "all" the members since these
131
+ // will reference anything that could be packaged. The "copyNodes" function will only include the type if it
132
+ // exists in the profile itself
133
+ this.otherProfileSettings.name.forEach((element) => {
134
+ const idx = this.otherProfileSettings.name.indexOf(element);
135
+ if (idx > -1) {
136
+ hasNodes =
137
+ this.copyNodes(profileDom, this.otherProfileSettings.parentElement[idx], this.otherProfileSettings.childElement[idx], allMembers, profileNode, profileName) ?? hasNodes;
138
+ }
139
+ });
140
+ // add userLicenses to the profile
141
+ if (this.includeUserLicenses) {
142
+ const userLicenses = profileDom.getElementsByTagName('userLicense');
143
+ if (userLicenses) {
144
+ hasNodes = true;
145
+ for (const userLicense of Array.from(userLicenses)) {
146
+ profileNode.appendChild(userLicense.cloneNode(true));
147
+ }
165
148
  }
166
149
  }
167
- try {
168
- fs.unlinkSync(destFilePath);
150
+ const xmlSrcFile = path.basename(profilePath);
151
+ const xmlFile = xmlSrcFile.replace(/(.*)(-meta.xml)/, '$1');
152
+ const destFilePath = path.join(destPath, xmlFile);
153
+ if (hasNodes) {
154
+ const serializer = new xmldom_1.XMLSerializer();
155
+ serializer.serializeToString(newDom);
156
+ fs.writeFileSync(destFilePath, serializer.serializeToString(newDom), 'utf-8');
169
157
  }
170
- catch (err) {
171
- // It is normal for the file to not exist if the profile is in the workspace but not in the directory being packaged.
172
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
173
- if (err.code !== 'ENOENT') {
174
- throw err;
158
+ else {
159
+ // remove from manifest
160
+ // eslint-disable-next-line @typescript-eslint/no-shadow
161
+ const profileName = xmlFile.replace(/(.*)(\.profile)/, '$1');
162
+ excludedProfiles.push(profileName);
163
+ if (this.generateProfileInformation) {
164
+ const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
165
+ if (profile) {
166
+ profile.setIsPackaged(false);
167
+ }
168
+ }
169
+ try {
170
+ fs.unlinkSync(destFilePath);
171
+ }
172
+ catch (err) {
173
+ // It is normal for the file to not exist if the profile is in the workspace but not in the directory being packaged.
174
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
175
+ if (err instanceof Error && 'code' in err && err.code !== 'ENOENT') {
176
+ throw err;
177
+ }
175
178
  }
176
179
  }
177
180
  }
@@ -187,22 +190,25 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
187
190
  filterAndGenerateProfilesForManifest(typesArr, excludedDirectories = []) {
188
191
  const profilePaths = this.findAllProfiles(excludedDirectories);
189
192
  // Filter all profiles
190
- typesArr = (typesArr || []).filter((kvp) => kvp.name[0] !== 'Profile');
193
+ const filteredTypesArr = (typesArr ?? []).filter((kvp) => kvp.name[0] !== 'Profile');
191
194
  if (profilePaths) {
192
195
  const members = [];
193
196
  profilePaths.forEach((profilePath) => {
194
197
  // profile metadata can present in any directory in the package structure
195
- const profileName = profilePath.match(/([^/]+)\.profile-meta.xml/)[1];
196
- members.push(profileName);
197
- if (this.generateProfileInformation) {
198
- this.profiles.push(new ProfileInformation(profileName, profilePath, true, []));
198
+ const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
199
+ const profileName = profileNameMatch ? profileNameMatch[1] : null;
200
+ if (profileName) {
201
+ members.push(profileName);
202
+ if (this.generateProfileInformation) {
203
+ this.profiles.push(new ProfileInformation(profileName, profilePath, true, []));
204
+ }
199
205
  }
200
206
  });
201
207
  if (members.length > 0) {
202
- typesArr.push({ name: ['Profile'], members });
208
+ filteredTypesArr.push({ name: ['Profile'], members });
203
209
  }
204
210
  }
205
- return typesArr;
211
+ return filteredTypesArr;
206
212
  }
207
213
  getProfileInformation() {
208
214
  return this.profiles;
@@ -216,18 +222,20 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
216
222
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
217
223
  for (let i = 0; i < nodes.length; i++) {
218
224
  const name = nodes[i].getElementsByTagName(childElement)[0].childNodes[0].nodeValue;
219
- if (members.includes(name)) {
220
- // appendChild will take the passed in node (newNode) and find the parent if it exists and then remove
221
- // the newNode from the parent. This causes issues with the way this is copying the nodes, so pass in a clone instead.
222
- const currentNode = nodes[i].cloneNode(true);
223
- appendToNode.appendChild(currentNode);
224
- nodesAdded = true;
225
- }
226
- else if (this.generateProfileInformation) {
227
- // Tell the user which profile setting has been removed from the package
228
- const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
229
- if (profile) {
230
- profile.appendRemovedSetting(name);
225
+ if (name) {
226
+ if (members.includes(name)) {
227
+ // appendChild will take the passed in node (newNode) and find the parent if it exists and then remove
228
+ // the newNode from the parent. This causes issues with the way this is copying the nodes, so pass in a clone instead.
229
+ const currentNode = nodes[i].cloneNode(true);
230
+ appendToNode.appendChild(currentNode);
231
+ nodesAdded = true;
232
+ }
233
+ else if (this.generateProfileInformation) {
234
+ // Tell the user which profile setting has been removed from the package
235
+ const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
236
+ if (profile) {
237
+ profile.appendRemovedSetting(name);
238
+ }
231
239
  }
232
240
  }
233
241
  }
@@ -17,7 +17,7 @@ const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_
17
17
  const pkgMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'package');
18
18
  async function getUninstallErrors(conn, id) {
19
19
  const errorQueryResult = await conn.tooling.query(`"SELECT Message FROM PackageVersionUninstallRequestError WHERE ParentRequest.Id = '${id}' ORDER BY Message"`);
20
- return errorQueryResult?.records || [];
20
+ return errorQueryResult?.records ?? [];
21
21
  }
22
22
  exports.getUninstallErrors = getUninstallErrors;
23
23
  async function pollUninstall(uninstallRequestId, conn, frequency, wait) {
@@ -58,17 +58,23 @@ async function uninstallPackage(id, conn, frequency = kit_1.Duration.seconds(0),
58
58
  const uninstallRequest = await conn.tooling.sobject('SubscriberPackageVersionUninstallRequest').create({
59
59
  SubscriberPackageVersionId: id,
60
60
  });
61
- if (wait.seconds === 0) {
62
- return (await conn.tooling
63
- .sobject('SubscriberPackageVersionUninstallRequest')
64
- .retrieve(uninstallRequest.id));
65
- }
66
- else {
67
- return await pollUninstall(uninstallRequest.id, conn, frequency, wait);
61
+ if (uninstallRequest.success) {
62
+ if (wait.seconds === 0) {
63
+ return (await conn.tooling
64
+ .sobject('SubscriberPackageVersionUninstallRequest')
65
+ .retrieve(uninstallRequest.id));
66
+ }
67
+ else {
68
+ return await pollUninstall(uninstallRequest.id, conn, frequency, wait);
69
+ }
68
70
  }
71
+ throw (0, packageUtils_1.combineSaveErrors)('SubscriberPackageVersionUninstallRequest', 'create', uninstallRequest.errors);
69
72
  }
70
73
  catch (err) {
71
- throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
74
+ if (err instanceof Error) {
75
+ throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
76
+ }
77
+ throw err;
72
78
  }
73
79
  }
74
80
  exports.uninstallPackage = uninstallPackage;