@salesforce/packaging 0.0.2 → 0.0.3

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.
@@ -0,0 +1,786 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2022, salesforce.com, inc.
4
+ * All rights reserved.
5
+ * Licensed under the BSD 3-Clause license.
6
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.formatDate = exports.getSoqlWhereClauseMaxLength = exports.pollForStatusWithInterval = exports._getPackageVersionCreateRequestApi = exports.findOrCreatePackage2 = exports.getPackageAliasesFromId = exports.convertCamelCaseStringToSentence = exports.getPackageIdFromAlias = exports.getConfigPackageDirectory = exports.getConfigPackageDirectories = exports.getPackage2VersionNumber = exports.concatVersion = exports.getAncestorIdHighestRelease = exports.validateAncestorId = exports.getAncestorId = exports.getInClauseItemsCount = exports.queryWithInConditionChunking = exports.getPackageVersionStrings = exports.getHasMetadataRemoved = exports.getContainerOptions = exports.getSubscriberPackageVersionId = exports.getPackage2TypeBy04t = exports.getPackage2Type = exports.getPackageVersionId = exports.applyErrorAction = exports.massageErrorMessage = exports.isErrorPackageNotAvailable = exports.isErrorFromSPVQueryRestriction = exports.validUrl = exports.validatePatchVersion = exports.validateVersionNumber = exports.validateIdNoThrow = exports.validateId = exports.BY_LABEL = exports.BY_PREFIX = exports.DEFAULT_PACKAGE_DIR = exports.POLL_INTERVAL_SECONDS = exports.SOQL_WHERE_CLAUSE_MAX_LENGTH = exports.INSTALL_URL_BASE = exports.VERSION_NUMBER_SEP = void 0;
10
+ const core_1 = require("@salesforce/core");
11
+ const kit_1 = require("@salesforce/kit");
12
+ const interfaces_1 = require("../interfaces");
13
+ const packageVersionCreateRequestApi_1 = require("../package/packageVersionCreateRequestApi");
14
+ const versionNumber_1 = require("./versionNumber");
15
+ var Package2VersionStatus = interfaces_1.PackagingSObjects.Package2VersionStatus;
16
+ core_1.Messages.importMessagesDirectory(__dirname);
17
+ const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'messages');
18
+ exports.VERSION_NUMBER_SEP = '.';
19
+ const INVALID_TYPE_REGEX = /[\w]*(sObject type '[A-Za-z]*Package[2]?[A-Za-z]*' is not supported)[\w]*/im;
20
+ const ID_REGISTRY = [
21
+ {
22
+ prefix: '0Ho',
23
+ label: 'Package Id',
24
+ },
25
+ {
26
+ prefix: '05i',
27
+ label: 'Package Version Id',
28
+ },
29
+ {
30
+ prefix: '08c',
31
+ label: 'Package Version Create Request Id',
32
+ },
33
+ {
34
+ prefix: '04t',
35
+ label: 'Subscriber Package Version Id',
36
+ },
37
+ ];
38
+ exports.INSTALL_URL_BASE = 'https://login.salesforce.com/packaging/installPackage.apexp?p0=';
39
+ // https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_soslsoql.htm
40
+ exports.SOQL_WHERE_CLAUSE_MAX_LENGTH = 4000;
41
+ exports.POLL_INTERVAL_SECONDS = 30;
42
+ exports.DEFAULT_PACKAGE_DIR = {
43
+ path: '',
44
+ package: '',
45
+ versionName: 'ver 0.1',
46
+ versionNumber: '0.1.0.NEXT',
47
+ default: true,
48
+ };
49
+ const logger = core_1.Logger.childFromRoot('packageUtils');
50
+ exports.BY_PREFIX = (() => {
51
+ return Object.fromEntries(ID_REGISTRY.map((id) => [id.prefix, { prefix: id.prefix, label: id.label }]));
52
+ })();
53
+ exports.BY_LABEL = (() => {
54
+ return Object.fromEntries(ID_REGISTRY.map((id) => [id.label.replace(/ /g, '_').toUpperCase(), { prefix: id.prefix, label: id.label }]));
55
+ })();
56
+ function validateId(idObj, value) {
57
+ if (!validateIdNoThrow(idObj, value)) {
58
+ throw messages.createError('invalidIdOrAlias', [
59
+ Array.isArray(idObj) ? idObj.map((e) => e.label).join(' or ') : idObj.label,
60
+ value,
61
+ Array.isArray(idObj) ? idObj.map((e) => e.prefix).join(' or ') : idObj.prefix,
62
+ ]);
63
+ }
64
+ }
65
+ exports.validateId = validateId;
66
+ function validateIdNoThrow(idObj, value) {
67
+ if (!value || (value.length !== 15 && value.length !== 18)) {
68
+ return false;
69
+ }
70
+ return Array.isArray(idObj) ? idObj.some((e) => value.startsWith(e.prefix)) : value.startsWith(idObj.prefix);
71
+ }
72
+ exports.validateIdNoThrow = validateIdNoThrow;
73
+ function validateVersionNumber(versionNumberString, supportedBuildNumberToken, supportedBuildNumberToken2) {
74
+ const versionNumber = versionNumber_1.VersionNumber.from('1.1.0.NEXT');
75
+ // build number can be a number or valid token
76
+ if (Number.isNaN(parseInt(versionNumber.build, 10)) &&
77
+ versionNumber.build !== supportedBuildNumberToken &&
78
+ versionNumber.build !== supportedBuildNumberToken2) {
79
+ if (supportedBuildNumberToken2) {
80
+ throw messages.createError('errorInvalidBuildNumberForKeywords', [
81
+ versionNumberString,
82
+ supportedBuildNumberToken,
83
+ supportedBuildNumberToken2,
84
+ ]);
85
+ }
86
+ else {
87
+ throw messages.createError('errorInvalidBuildNumber', [versionNumberString, supportedBuildNumberToken]);
88
+ }
89
+ }
90
+ return versionNumberString;
91
+ }
92
+ exports.validateVersionNumber = validateVersionNumber;
93
+ async function validatePatchVersion(connection, versionNumberString, packageId) {
94
+ const query = `SELECT ContainerOptions FROM Package2 WHERE id ='${packageId}'`;
95
+ const queryResult = await connection.tooling.query(query);
96
+ if (queryResult.records === null || queryResult.records.length === 0) {
97
+ throw messages.createError('errorInvalidPackageId', [packageId]);
98
+ }
99
+ // Enforce a patch version of zero (0) for Locked packages only
100
+ if (queryResult.records[0].ContainerOptions === 'Locked') {
101
+ const versionNumber = versionNumber_1.VersionNumber.from(versionNumberString);
102
+ if (versionNumber.patch !== '0') {
103
+ throw messages.createError('errorInvalidPatchNumber', [versionNumberString]);
104
+ }
105
+ }
106
+ }
107
+ exports.validatePatchVersion = validatePatchVersion;
108
+ // TODO: let's get rid of this in favor of SfdcUrl.isValidUrl()
109
+ // check that the provided url has a valid format
110
+ function validUrl(url) {
111
+ return core_1.SfdcUrl.isValidUrl(url);
112
+ }
113
+ exports.validUrl = validUrl;
114
+ // determines if error is from malformed SubscriberPackageVersion query
115
+ // this is in place to allow cli to run against app version 214, where SPV queries
116
+ // do not require installation key
117
+ function isErrorFromSPVQueryRestriction(err) {
118
+ return (err.name === 'MALFORMED_QUERY' &&
119
+ err.message.includes('Implementation restriction: You can only perform queries of the form Id'));
120
+ }
121
+ exports.isErrorFromSPVQueryRestriction = isErrorFromSPVQueryRestriction;
122
+ function isErrorPackageNotAvailable(err) {
123
+ return err.name === 'UNKNOWN_EXCEPTION' || err.name === 'PACKAGE_UNAVAILABLE';
124
+ }
125
+ exports.isErrorPackageNotAvailable = isErrorPackageNotAvailable;
126
+ // overwrites error message under certain conditions
127
+ function massageErrorMessage(err) {
128
+ if (err.name === 'INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST') {
129
+ err['message'] = messages.getMessage('invalidPackageTypeMessage');
130
+ }
131
+ if (err.name === 'MALFORMED_ID' &&
132
+ (err.message.includes('Version ID') || err.message.includes('Version Definition ID'))) {
133
+ err['message'] = messages.getMessage('malformedPackageVersionIdMessage');
134
+ }
135
+ if (err.name === 'MALFORMED_ID' && err.message.includes('Package2 ID')) {
136
+ err['message'] = messages.getMessage('malformedPackageIdMessage');
137
+ }
138
+ // remove references to Second Generation
139
+ if (err.message.includes('Second Generation ')) {
140
+ err['message'] = err.message.replace('Second Generation ', '');
141
+ }
142
+ return err;
143
+ }
144
+ exports.massageErrorMessage = massageErrorMessage;
145
+ // applies actions to common package errors
146
+ // eslint-disable-next-line complexity
147
+ function applyErrorAction(err) {
148
+ // append when actions already exist
149
+ const actions = [];
150
+ // include existing actions
151
+ if (err['action']) {
152
+ actions.push(err['action']);
153
+ }
154
+ // TODO: (need to get with packaging team on this)
155
+ // until next generation packaging is GA, wrap perm-based errors w/
156
+ // 'contact sfdc' action (REMOVE once GA'd)
157
+ if ((err.name === 'INVALID_TYPE' && INVALID_TYPE_REGEX.test(err.message)) ||
158
+ (err.name === 'NOT_FOUND' && err.message === messages.getMessage('notFoundMessage'))) {
159
+ // contact sfdc customer service
160
+ actions.push(messages.getMessage('packageNotEnabledAction'));
161
+ }
162
+ if (err.name === 'INVALID_FIELD' && err.message.includes('Instance')) {
163
+ actions.push(messages.getMessage('packageInstanceNotEnabled'));
164
+ }
165
+ if (err.name === 'INVALID_FIELD' && err.message.includes('SourceOrg')) {
166
+ actions.push(messages.getMessage('packageSourceOrgNotEnabled'));
167
+ }
168
+ if (err.name === 'INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST') {
169
+ actions.push(messages.getMessage('invalidPackageTypeAction'));
170
+ }
171
+ if (err.name === 'MALFORMED_ID' && err.message === messages.getMessage('malformedPackageIdMessage')) {
172
+ actions.push(messages.getMessage('malformedPackageIdAction'));
173
+ }
174
+ if (err.name === 'MALFORMED_ID' && err.message === messages.getMessage('malformedPackageVersionIdMessage')) {
175
+ actions.push(messages.getMessage('malformedPackageVersionIdAction'));
176
+ }
177
+ if ((err.message.includes(exports.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID.label) && err.message.includes('is invalid')) ||
178
+ err.name === 'INVALID_ID_FIELD' ||
179
+ (err.name === 'INVALID_INPUT' && err.message.includes('Verify you entered the correct ID')) ||
180
+ err.name === 'MALFORMED_ID') {
181
+ actions.push(messages.getMessage('idNotFoundAction'));
182
+ }
183
+ if (actions.length > 0) {
184
+ err['action'] = actions.join('\n');
185
+ }
186
+ return err;
187
+ }
188
+ exports.applyErrorAction = applyErrorAction;
189
+ /**
190
+ * Given a subscriber package version ID (04t) or package version ID (05i), return the package version ID (05i)
191
+ *
192
+ * @param versionId The subscriber package version ID
193
+ * @param connection For tooling query
194
+ */
195
+ async function getPackageVersionId(versionId, connection) {
196
+ // if it's already a 05i return it, otherwise query for it
197
+ if (versionId === null || versionId === void 0 ? void 0 : versionId.startsWith(exports.BY_LABEL.PACKAGE_VERSION_ID.prefix)) {
198
+ return versionId;
199
+ }
200
+ const query = `SELECT Id FROM Package2Version WHERE SubscriberPackageVersionId = '${versionId}'`;
201
+ return connection.tooling.query(query).then((queryResult) => {
202
+ if (!queryResult || !queryResult.totalSize) {
203
+ throw messages.createError('errorInvalidIdNoMatchingVersionId', [
204
+ exports.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID.label,
205
+ versionId,
206
+ exports.BY_LABEL.PACKAGE_VERSION_ID.label,
207
+ ]);
208
+ }
209
+ return queryResult.records[0].Id;
210
+ });
211
+ }
212
+ exports.getPackageVersionId = getPackageVersionId;
213
+ /**
214
+ * Given 0Ho the package type type (Managed, Unlocked, Locked(deprecated?))
215
+ *
216
+ * @param package2Id the 0Ho
217
+ * @param connection For tooling query
218
+ * @throws Error with message when package2 cannot be found
219
+ */
220
+ async function getPackage2Type(package2Id, connection) {
221
+ const query = `SELECT ContainerOptions FROM Package2 WHERE id ='${package2Id}'`;
222
+ const queryResult = await connection.tooling.query(query);
223
+ if (!queryResult || queryResult.records === null || queryResult.records.length === 0) {
224
+ throw messages.createError('errorInvalidPackageId', [package2Id]);
225
+ }
226
+ return queryResult.records[0].ContainerOptions;
227
+ }
228
+ exports.getPackage2Type = getPackage2Type;
229
+ /**
230
+ * Given 04t the package type type (Managed, Unlocked, Locked(deprecated?))
231
+ *
232
+ * @param package2VersionId the 04t
233
+ * @param connection For tooling query
234
+ * @param installKey For tooling query, if an installation key is applicable to the package version it must be passed in the queries
235
+ * @throws Error with message when package2 cannot be found
236
+ */
237
+ async function getPackage2TypeBy04t(package2VersionId, connection, installKey) {
238
+ let query = `SELECT Package2ContainerOptions FROM SubscriberPackageVersion WHERE id ='${package2VersionId}'`;
239
+ if (installKey) {
240
+ const escapedInstallationKey = installKey.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
241
+ query += ` AND InstallationKey ='${escapedInstallationKey}'`;
242
+ }
243
+ const queryResult = await connection.tooling.query(query);
244
+ if (!queryResult || queryResult.records === null || queryResult.records.length === 0) {
245
+ throw messages.createError('errorInvalidPackageId', [package2VersionId]);
246
+ }
247
+ return queryResult.records[0].Package2ContainerOptions;
248
+ }
249
+ exports.getPackage2TypeBy04t = getPackage2TypeBy04t;
250
+ /**
251
+ * Given a package version ID (05i) or subscriber package version ID (04t), return the subscriber package version ID (04t)
252
+ *
253
+ * @param versionId The suscriber package version ID
254
+ * @param connection For tooling query
255
+ */
256
+ async function getSubscriberPackageVersionId(versionId, connection) {
257
+ // if it's already a 04t return it, otherwise query for it
258
+ if (!versionId || versionId.startsWith(exports.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID.prefix)) {
259
+ return versionId;
260
+ }
261
+ const query = `SELECT SubscriberPackageVersionId FROM Package2Version WHERE Id = '${versionId}'`;
262
+ const queryResult = await connection.tooling.query(query);
263
+ if (!queryResult || !queryResult.totalSize) {
264
+ throw messages.createError('errorInvalidIdNoMatchingVersionId', [
265
+ exports.BY_LABEL.PACKAGE_VERSION_ID.label,
266
+ versionId,
267
+ exports.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID.label,
268
+ ]);
269
+ }
270
+ return queryResult.records[0].SubscriberPackageVersionId;
271
+ }
272
+ exports.getSubscriberPackageVersionId = getSubscriberPackageVersionId;
273
+ /**
274
+ * Get the ContainerOptions for the specified Package2 (0Ho) IDs.
275
+ *
276
+ * @return Map of 0Ho id to container option api value
277
+ * @param package2Ids The list of package IDs
278
+ * @param connection For tooling query
279
+ */
280
+ // eslint-disable-next-line @typescript-eslint/require-await
281
+ async function getContainerOptions(package2Ids, connection) {
282
+ if (!package2Ids || package2Ids.length === 0) {
283
+ return new Map();
284
+ }
285
+ const query = 'SELECT Id, ContainerOptions FROM Package2 WHERE Id IN (%IDS%)';
286
+ const records = await queryWithInConditionChunking(query, package2Ids, '%IDS%', connection);
287
+ if (records && records.length > 0) {
288
+ return new Map(records.map((record) => [record.Id, record.ContainerOptions]));
289
+ }
290
+ return new Map();
291
+ }
292
+ exports.getContainerOptions = getContainerOptions;
293
+ /**
294
+ * Return the Package2Version.HasMetadataRemoved field value for the given Id (05i)
295
+ *
296
+ * @param packageVersionId package version ID (05i)
297
+ * @param connection For tooling query
298
+ */
299
+ async function getHasMetadataRemoved(packageVersionId, connection) {
300
+ const query = `SELECT HasMetadataRemoved FROM Package2Version WHERE Id = '${packageVersionId}'`;
301
+ const queryResult = await connection.tooling.query(query);
302
+ if (!queryResult || queryResult.records === null || queryResult.records.length === 0) {
303
+ throw messages.createError('errorInvalidIdNoMatchingVersionId', [
304
+ exports.BY_LABEL.PACKAGE_VERSION_ID.label,
305
+ packageVersionId,
306
+ exports.BY_LABEL.PACKAGE_VERSION_ID.label,
307
+ ]);
308
+ }
309
+ return queryResult.records[0].HasMetadataRemoved;
310
+ }
311
+ exports.getHasMetadataRemoved = getHasMetadataRemoved;
312
+ /**
313
+ * Given a list of subscriber package version IDs (04t), return the associated version strings (e.g., Major.Minor.Patch.Build)
314
+ *
315
+ * @return Map of subscriberPackageVersionId to versionString
316
+ * @param subscriberPackageVersionIds
317
+ * @param connection For tooling query
318
+ */
319
+ // eslint-disable-next-line @typescript-eslint/require-await
320
+ async function getPackageVersionStrings(subscriberPackageVersionIds, connection) {
321
+ let results = new Map();
322
+ if (!subscriberPackageVersionIds || subscriberPackageVersionIds.length === 0) {
323
+ return results;
324
+ }
325
+ // remove any duplicate Ids
326
+ const ids = [...new Set(subscriberPackageVersionIds)];
327
+ const query = 'SELECT SubscriberPackageVersionId, MajorVersion, MinorVersion, PatchVersion, BuildNumber FROM Package2Version WHERE SubscriberPackageVersionId IN (%IDS%)';
328
+ const records = await queryWithInConditionChunking(query, ids, '%IDS%', connection);
329
+ if (records && records.length > 0) {
330
+ results = new Map(records.map((record) => {
331
+ const version = concatVersion(record.MajorVersion, record.MinorVersion, record.PatchVersion, record.BuildNumber);
332
+ return [record.SubscriberPackageVersionId, version];
333
+ }));
334
+ }
335
+ return results;
336
+ }
337
+ exports.getPackageVersionStrings = getPackageVersionStrings;
338
+ /**
339
+ * For queries with an IN condition, determine if the WHERE clause will exceed
340
+ * SOQL's 4000 character limit. Perform multiple queries as needed to stay below the limit.
341
+ *
342
+ * @return concatenated array of records returned from the resulting query(ies)
343
+ * @param query The full query to execute containing the replaceToken param in its IN clause
344
+ * @param items The IN clause items. A length-appropriate single-quoted comma-separated string chunk will be made from the items.
345
+ * @param replaceToken A placeholder in the query's IN condition that will be replaced with the chunked items
346
+ * @param connection For tooling query
347
+ */
348
+ async function queryWithInConditionChunking(query, items, replaceToken, connection) {
349
+ let records = [];
350
+ if (!query || !items || !replaceToken) {
351
+ return records;
352
+ }
353
+ const whereClause = query.substring(query.toLowerCase().indexOf('where'), query.length);
354
+ const inClauseItemsMaxLength = exports.SOQL_WHERE_CLAUSE_MAX_LENGTH - whereClause.length - replaceToken.length;
355
+ let itemsQueried = 0;
356
+ while (itemsQueried < items.length) {
357
+ const chunkCount = getInClauseItemsCount(items, itemsQueried, inClauseItemsMaxLength);
358
+ if (chunkCount === 0) {
359
+ throw messages.createError('itemDoesNotFitWithinMaxLength', [
360
+ query,
361
+ items[itemsQueried].slice(0, 20),
362
+ items[itemsQueried].length,
363
+ inClauseItemsMaxLength,
364
+ ]);
365
+ }
366
+ const itemsStr = `${items.slice(itemsQueried, itemsQueried + chunkCount).join("','")}`;
367
+ const queryChunk = query.replace(replaceToken, itemsStr);
368
+ const result = await connection.tooling.query(queryChunk);
369
+ if (result && result.records.length > 0) {
370
+ records = records.concat(result.records);
371
+ }
372
+ itemsQueried += chunkCount;
373
+ }
374
+ return records;
375
+ }
376
+ exports.queryWithInConditionChunking = queryWithInConditionChunking;
377
+ /**
378
+ * Returns the number of items that can be included in a quoted comma-separated string (e.g., "'item1','item2'") not exceeding maxLength
379
+ */
380
+ // TODO: this function cannot handle a single item that is longer than maxLength - what to do, since this could be the root cause of an infinite loop?
381
+ function getInClauseItemsCount(items, startIndex, maxLength) {
382
+ let resultLength = 0;
383
+ let includedCount = 0;
384
+ while (startIndex + includedCount < items.length) {
385
+ let itemLength = 0;
386
+ if (items[startIndex + includedCount]) {
387
+ itemLength = items[startIndex + includedCount].length + 3; // 3 = length of "'',"
388
+ if (resultLength + itemLength > maxLength) {
389
+ // the limit has been exceeded, return the current count
390
+ return includedCount;
391
+ }
392
+ includedCount++;
393
+ resultLength += itemLength;
394
+ }
395
+ }
396
+ return includedCount;
397
+ }
398
+ exports.getInClauseItemsCount = getInClauseItemsCount;
399
+ /**
400
+ * Given a package descriptor, return the ancestor ID. This code was duplicated to scratchOrgInfoGenerator.getAncestorIds,
401
+ * changes here may need to be duplicated there until that code, and/or this code is moved to a separate plugin.
402
+ *
403
+ * @param packageDescriptorJson JSON for packageDirectories element in sfdx-project.json
404
+ * @param connection For tooling query
405
+ * @param project The project to use for looking up the ancestor ID
406
+ * @param versionNumberString The version number string to use for looking up the ancestor ID
407
+ * @param skipAncestorCheck If true, skip the check for the ancestor ID
408
+ */
409
+ async function getAncestorId(packageDescriptorJson, connection, project, versionNumberString, skipAncestorCheck) {
410
+ // eslint-disable-next-line complexity
411
+ return Promise.resolve().then(async () => {
412
+ var _a;
413
+ // If an id property is present, use it. Otherwise, look up the package id from the package property.
414
+ const packageId = (_a = packageDescriptorJson['id']) !== null && _a !== void 0 ? _a : getPackageIdFromAlias(packageDescriptorJson.package, project);
415
+ // No need to proceed if Unlocked
416
+ const packageType = await getPackage2Type(packageId, connection);
417
+ if (packageType === 'Unlocked') {
418
+ return '';
419
+ }
420
+ let ancestorId = '';
421
+ // ancestorID can be alias, 05i, or 04t;
422
+ // validate and convert to 05i, as needed
423
+ const versionNumber = versionNumberString.split(exports.VERSION_NUMBER_SEP);
424
+ const isPatch = versionNumber[2] !== '0';
425
+ let origSpecifiedAncestor = packageDescriptorJson.ancestorId;
426
+ let highestReleasedVersion = null;
427
+ const explicitUseHighestRelease = packageDescriptorJson.ancestorId === versionNumber_1.BuildNumberToken.HIGHEST_VERSION_NUMBER_TOKEN ||
428
+ packageDescriptorJson.ancestorVersion === versionNumber_1.BuildNumberToken.HIGHEST_VERSION_NUMBER_TOKEN;
429
+ const explicitUseNoAncestor = packageDescriptorJson.ancestorId === versionNumber_1.BuildNumberToken.NONE_VERSION_NUMBER_TOKEN ||
430
+ packageDescriptorJson.ancestorVersion === versionNumber_1.BuildNumberToken.NONE_VERSION_NUMBER_TOKEN;
431
+ if ((explicitUseHighestRelease || explicitUseNoAncestor) &&
432
+ packageDescriptorJson.ancestorId &&
433
+ packageDescriptorJson.ancestorVersion) {
434
+ if (packageDescriptorJson.ancestorId !== packageDescriptorJson.ancestorVersion) {
435
+ // both ancestorId and ancestorVersion specified, HIGHEST and/or NONE are used, the values disagree
436
+ throw messages.createError('errorAncestorIdVersionHighestOrNoneMismatch', [
437
+ packageDescriptorJson.ancestorId,
438
+ packageDescriptorJson.ancestorVersion,
439
+ ]);
440
+ }
441
+ }
442
+ if (explicitUseNoAncestor && skipAncestorCheck) {
443
+ return '';
444
+ }
445
+ else {
446
+ const result = await getAncestorIdHighestRelease(connection, packageId, versionNumberString, explicitUseHighestRelease, skipAncestorCheck);
447
+ if (result.finalAncestorId) {
448
+ return result.finalAncestorId;
449
+ }
450
+ highestReleasedVersion = result.highestReleasedVersion;
451
+ }
452
+ // at this point if explicitUseHighestRelease=true, we have returned the ancestorId or thrown an error
453
+ // highestReleasedVersion should be null only if skipAncestorCheck or if there is no existing released package version
454
+ if (!explicitUseNoAncestor && packageDescriptorJson.ancestorId) {
455
+ ancestorId = getPackageIdFromAlias(packageDescriptorJson.ancestorId, project);
456
+ validateId([exports.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID, exports.BY_LABEL.PACKAGE_VERSION_ID], ancestorId);
457
+ ancestorId = await getPackageVersionId(ancestorId, connection);
458
+ }
459
+ if (!explicitUseNoAncestor && packageDescriptorJson.ancestorVersion) {
460
+ const regNumbers = new RegExp('^[0-9]+$');
461
+ const versionNumber = packageDescriptorJson.ancestorVersion.split(exports.VERSION_NUMBER_SEP);
462
+ if (versionNumber.length < 3 ||
463
+ versionNumber.length > 4 ||
464
+ !versionNumber[0].match(regNumbers) ||
465
+ !versionNumber[1].match(regNumbers) ||
466
+ !versionNumber[2].match(regNumbers)) {
467
+ throw messages.createError('errorInvalidAncestorVersionFormat', [packageDescriptorJson.ancestorVersion]);
468
+ }
469
+ const query = 'SELECT Id, IsReleased FROM Package2Version ' +
470
+ `WHERE Package2Id = '${packageId}' AND MajorVersion = ${versionNumber[0]} AND MinorVersion = ${versionNumber[1]} AND PatchVersion = ${versionNumber[2]}`;
471
+ let queriedAncestorId;
472
+ const ancestorVersionResult = await connection.tooling.query(query);
473
+ if (!ancestorVersionResult || !ancestorVersionResult.totalSize) {
474
+ throw messages.createError('errorNoMatchingAncestor', [packageDescriptorJson.ancestorVersion, packageId]);
475
+ }
476
+ else {
477
+ const releasedAncestor = ancestorVersionResult.records.find((rec) => rec.IsReleased === true);
478
+ if (!releasedAncestor) {
479
+ throw messages.createError('errorAncestorNotReleased', [packageDescriptorJson.ancestorVersion]);
480
+ }
481
+ else {
482
+ queriedAncestorId = releasedAncestor.Id;
483
+ }
484
+ }
485
+ // check for discrepancy between queried ancestorId and descriptor's ancestorId
486
+ if (Object.prototype.hasOwnProperty.call(packageDescriptorJson, 'ancestorId') &&
487
+ ancestorId !== queriedAncestorId) {
488
+ throw messages.createError('errorAncestorIdVersionMismatch', [
489
+ packageDescriptorJson.ancestorVersion,
490
+ packageDescriptorJson.ancestorId,
491
+ ]);
492
+ }
493
+ ancestorId = queriedAncestorId;
494
+ origSpecifiedAncestor = packageDescriptorJson.ancestorVersion;
495
+ }
496
+ return validateAncestorId(ancestorId, highestReleasedVersion, explicitUseNoAncestor, isPatch, skipAncestorCheck, origSpecifiedAncestor);
497
+ });
498
+ }
499
+ exports.getAncestorId = getAncestorId;
500
+ function validateAncestorId(ancestorId, highestReleasedVersion, explicitUseNoAncestor, isPatch, skipAncestorCheck, origSpecifiedAncestor) {
501
+ if (explicitUseNoAncestor) {
502
+ if (!highestReleasedVersion) {
503
+ return '';
504
+ }
505
+ else {
506
+ // the explicitUseNoAncestor && skipAncestorCheck case is handled above
507
+ throw messages.createError('errorAncestorNoneNotAllowed', [getPackage2VersionNumber(highestReleasedVersion)]);
508
+ }
509
+ }
510
+ if (!isPatch && !skipAncestorCheck) {
511
+ if (highestReleasedVersion) {
512
+ if (highestReleasedVersion.Id !== ancestorId) {
513
+ throw messages.createError('errorAncestorNotHighest', [
514
+ origSpecifiedAncestor,
515
+ getPackage2VersionNumber(highestReleasedVersion),
516
+ ]);
517
+ }
518
+ }
519
+ else {
520
+ // looks like the initial version:create - allow
521
+ ancestorId = '';
522
+ }
523
+ }
524
+ return ancestorId;
525
+ }
526
+ exports.validateAncestorId = validateAncestorId;
527
+ async function getAncestorIdHighestRelease(connection, packageId, versionNumberString, explicitUseHighestRelease, skipAncestorCheck) {
528
+ const versionNumber = versionNumberString.split(exports.VERSION_NUMBER_SEP);
529
+ const isPatch = versionNumber[2] !== '0';
530
+ const result = { finalAncestorId: null, highestReleasedVersion: null };
531
+ if (isPatch && explicitUseHighestRelease) {
532
+ // based on server-side validation, whatever ancestor is specified for a patch is
533
+ // tightly controlled; therefore we only need concern ourselves if explicitUseHighestRelease == true;
534
+ // equally applies when skipAncestorCheck == true
535
+ // gather appropriate matching major.minor.0
536
+ const query = `SELECT Id FROM Package2Version WHERE Package2Id = '${packageId}' ` +
537
+ 'AND IsReleased = True AND IsDeprecated = False AND PatchVersion = 0 ' +
538
+ `AND MajorVersion = ${versionNumber[0]} AND MinorVersion = ${versionNumber[1]} ` +
539
+ 'ORDER BY MajorVersion Desc, MinorVersion Desc, PatchVersion Desc, BuildNumber Desc LIMIT 1';
540
+ const majorMinorVersionResult = await connection.tooling.query(query);
541
+ const majorMinorVersionRecords = majorMinorVersionResult.records;
542
+ if (majorMinorVersionRecords && (majorMinorVersionRecords === null || majorMinorVersionRecords === void 0 ? void 0 : majorMinorVersionRecords.length) === 1 && majorMinorVersionRecords[0]) {
543
+ result.finalAncestorId = majorMinorVersionRecords[0].Id;
544
+ }
545
+ else {
546
+ const majorMinorNotFound = `${versionNumber[0]}.${versionNumber[1]}.0`;
547
+ throw messages.createError('errorNoMatchingMajorMinorForPatch', [majorMinorNotFound]);
548
+ }
549
+ }
550
+ else if (!isPatch && (explicitUseHighestRelease || !skipAncestorCheck)) {
551
+ // ancestor must be set to latest released major.minor version
552
+ const query = 'SELECT Id, SubscriberPackageVersionId, MajorVersion, MinorVersion, PatchVersion FROM Package2Version ' +
553
+ `WHERE Package2Id = '${packageId}' AND IsReleased = True AND IsDeprecated = False AND PatchVersion = 0 ` +
554
+ 'ORDER BY MajorVersion Desc, MinorVersion Desc, PatchVersion Desc, BuildNumber Desc LIMIT 1';
555
+ const highestVersionResult = await connection.tooling.query(query);
556
+ const highestVersionRecords = highestVersionResult.records;
557
+ if (highestVersionRecords && highestVersionRecords[0]) {
558
+ result.highestReleasedVersion = highestVersionRecords[0];
559
+ if (explicitUseHighestRelease) {
560
+ result.finalAncestorId = result.highestReleasedVersion.Id;
561
+ }
562
+ }
563
+ else if (explicitUseHighestRelease) {
564
+ // there is no eligible ancestor version
565
+ throw new Error(messages.getMessage('errorNoMatchingAncestor', [versionNumberString, packageId]));
566
+ }
567
+ }
568
+ return result;
569
+ }
570
+ exports.getAncestorIdHighestRelease = getAncestorIdHighestRelease;
571
+ /**
572
+ * Return a version string in Major.Minor.Patch.Build format, using 0 for any empty part
573
+ */
574
+ function concatVersion(major, minor, patch, build) {
575
+ return [major, minor, patch, build].map((part) => (part ? `${part}` : '0')).join('.');
576
+ }
577
+ exports.concatVersion = concatVersion;
578
+ function getPackage2VersionNumber(package2VersionObj) {
579
+ const version = concatVersion(package2VersionObj.MajorVersion, package2VersionObj.MinorVersion, package2VersionObj.PatchVersion, undefined);
580
+ return version.slice(0, version.lastIndexOf('.'));
581
+ }
582
+ exports.getPackage2VersionNumber = getPackage2VersionNumber;
583
+ // TODO: get rid of this function if possible.
584
+ function getConfigPackageDirectories(project) {
585
+ return project.getPackageDirectories();
586
+ }
587
+ exports.getConfigPackageDirectories = getConfigPackageDirectories;
588
+ function getConfigPackageDirectory(packageDirs, lookupProperty, lookupValue) {
589
+ return packageDirs === null || packageDirs === void 0 ? void 0 : packageDirs.find((pkgDir) => pkgDir[lookupProperty] === lookupValue);
590
+ }
591
+ exports.getConfigPackageDirectory = getConfigPackageDirectory;
592
+ /**
593
+ * Given a packageAlias, attempt to return the associated id from the config
594
+ *
595
+ * @param packageAlias string representing a package alias
596
+ * @param project for obtaining the project config
597
+ * @returns the associated id or the arg given.
598
+ */
599
+ function getPackageIdFromAlias(packageAlias, project) {
600
+ const packageAliases = project.getSfProjectJson().getContents().packageAliases || {};
601
+ // return alias if it exists, otherwise return what was passed in
602
+ return packageAliases[packageAlias] || packageAlias;
603
+ }
604
+ exports.getPackageIdFromAlias = getPackageIdFromAlias;
605
+ /**
606
+ * @param stringIn pascal or camel case string
607
+ * @returns space delimited and lower-cased (except for 1st char) string (e.g. in "AbcdEfghIj" => "Abcd efgh ij")
608
+ */
609
+ function convertCamelCaseStringToSentence(stringIn) {
610
+ return (0, kit_1.camelCaseToTitleCase)(stringIn);
611
+ }
612
+ exports.convertCamelCaseStringToSentence = convertCamelCaseStringToSentence;
613
+ /**
614
+ * Given a package id, attempt to return the associated aliases from the config
615
+ *
616
+ * @param packageId string representing a package id
617
+ * @param project for obtaining the project config
618
+ * @returns an array of alias for the given id.
619
+ */
620
+ function getPackageAliasesFromId(packageId, project) {
621
+ const packageAliases = project.getSfProjectJson().getContents().packageAliases || {};
622
+ // check for a matching alias
623
+ return Object.entries(packageAliases)
624
+ .filter((alias) => alias[1] === packageId)
625
+ .map((alias) => alias[0]);
626
+ }
627
+ exports.getPackageAliasesFromId = getPackageAliasesFromId;
628
+ async function findOrCreatePackage2(seedPackage, connection) {
629
+ const query = `SELECT Id FROM Package2 WHERE ConvertedFromPackageId = '${seedPackage}'`;
630
+ const queryResult = await connection.tooling.query(query);
631
+ const records = queryResult.records;
632
+ if (records && records.length > 1) {
633
+ const ids = records.map((r) => r.Id);
634
+ throw new Error(messages.getMessage('errorMoreThanOnePackage2WithSeed', [ids.join(', ')]));
635
+ }
636
+ if (records && records.length === 1) {
637
+ // return the package2 object
638
+ return records[0].Id;
639
+ }
640
+ // Need to create a new Package2
641
+ const subQuery = `SELECT Name, Description, NamespacePrefix FROM SubscriberPackage WHERE Id = '${seedPackage}'`;
642
+ const subscriberResult = await connection.tooling.query(subQuery);
643
+ const subscriberRecords = subscriberResult.records;
644
+ if (!subscriberRecords || subscriberRecords.length <= 0) {
645
+ throw new Error(messages.getMessage('errorNoSubscriberPackageRecord', [seedPackage]));
646
+ }
647
+ const request = {
648
+ Name: subscriberRecords[0].Name,
649
+ Description: subscriberRecords[0].Description,
650
+ NamespacePrefix: subscriberRecords[0].NamespacePrefix,
651
+ ContainerOptions: 'Managed',
652
+ ConvertedFromPackageId: seedPackage,
653
+ };
654
+ const createResult = await connection.tooling.create('Package2', request);
655
+ if (!createResult.success) {
656
+ throw new Error(createResult.errors.map((e) => e.message).join('\n'));
657
+ }
658
+ return createResult.id;
659
+ }
660
+ exports.findOrCreatePackage2 = findOrCreatePackage2;
661
+ function _getPackageVersionCreateRequestApi(connection) {
662
+ return new packageVersionCreateRequestApi_1.PackageVersionCreateRequestApi({ connection });
663
+ }
664
+ exports._getPackageVersionCreateRequestApi = _getPackageVersionCreateRequestApi;
665
+ async function pollForStatusWithInterval(context, id, retries, packageId, branch, withProject, connection, interval) {
666
+ let remainingRetries = retries;
667
+ const pollingClient = await core_1.PollingClient.create({
668
+ poll: async () => {
669
+ var _a;
670
+ const pvcrApi = new packageVersionCreateRequestApi_1.PackageVersionCreateRequestApi({ connection });
671
+ const results = await pvcrApi.byId(id);
672
+ if (_isStatusEqualTo(results, [Package2VersionStatus.success, Package2VersionStatus.error])) {
673
+ // complete
674
+ if (_isStatusEqualTo(results, [Package2VersionStatus.success])) {
675
+ // update sfdx-project.json
676
+ let projectUpdated = false;
677
+ if (withProject && !process.env.SFDX_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_VERSION_CREATE) {
678
+ projectUpdated = true;
679
+ const query = `SELECT MajorVersion, MinorVersion, PatchVersion, BuildNumber FROM Package2Version WHERE Id = '${results[0].Package2VersionId}'`;
680
+ const package2VersionVersionString = await connection.tooling
681
+ .query(query)
682
+ .then((pkgQueryResult) => {
683
+ const record = pkgQueryResult.records[0];
684
+ return `${record.MajorVersion}.${record.MinorVersion}.${record.PatchVersion}-${record.BuildNumber}`;
685
+ });
686
+ const newConfig = await _generatePackageAliasEntry(connection, withProject, results[0].SubscriberPackageVersionId, package2VersionVersionString, branch, packageId);
687
+ withProject.getSfProjectJson().set('packageAliases', newConfig);
688
+ await withProject.getSfProjectJson().write();
689
+ }
690
+ core_1.Lifecycle.getInstance().emit(Package2VersionStatus.success, {
691
+ id,
692
+ package2VersionCreateRequestResult: results[0],
693
+ projectUpdated,
694
+ });
695
+ return { completed: true, payload: results[0] };
696
+ }
697
+ else {
698
+ let status = 'Unknown Error';
699
+ if ((results === null || results === void 0 ? void 0 : results.length) > 0 && results[0].Error.length > 0) {
700
+ const errors = [];
701
+ // for multiple errors, display one per line prefixed with (x)
702
+ if (results[0].Error.length > 1) {
703
+ results[0].Error.forEach((error) => {
704
+ errors.push(`(${errors.length + 1}) ${error}`);
705
+ });
706
+ errors.unshift(messages.getMessage('versionCreateFailedWithMultipleErrors'));
707
+ }
708
+ status = errors.length !== 0 ? errors.join('\n') : results[0].Error.join('\n');
709
+ }
710
+ core_1.Lifecycle.getInstance().emit(Package2VersionStatus.error, { id, status });
711
+ throw new core_1.SfError(status);
712
+ }
713
+ }
714
+ else {
715
+ const remainingTime = kit_1.Duration.milliseconds(interval.milliseconds * remainingRetries);
716
+ core_1.Lifecycle.getInstance().emit(Package2VersionStatus.inProgress, {
717
+ id,
718
+ package2VersionCreateRequestResult: results[0],
719
+ message: '',
720
+ remainingTime,
721
+ });
722
+ logger.info(`Request in progress. Sleeping ${interval} seconds. Will wait a total of ${remainingTime.seconds} more seconds before timing out. Current Status='${convertCamelCaseStringToSentence((_a = results[0]) === null || _a === void 0 ? void 0 : _a.Status)}'`);
723
+ remainingRetries--;
724
+ return { completed: false, payload: results[0] };
725
+ }
726
+ },
727
+ frequency: kit_1.Duration.milliseconds(interval.milliseconds * 1000),
728
+ timeout: kit_1.Duration.milliseconds(interval.milliseconds * retries * 1000),
729
+ });
730
+ return pollingClient.subscribe();
731
+ }
732
+ exports.pollForStatusWithInterval = pollForStatusWithInterval;
733
+ /**
734
+ * Generate package alias json entry for this package version that can be written to sfdx-project.json
735
+ *
736
+ * @param connection
737
+ * @param project SfProject instance for the project
738
+ * @param packageVersionId 04t id of the package to create the alias entry for
739
+ * @param packageVersionNumber that will be appended to the package name to form the alias
740
+ * @param branch
741
+ * @param packageId the 0Ho id
742
+ * @private
743
+ */
744
+ async function _generatePackageAliasEntry(connection, project, packageVersionId, packageVersionNumber, branch, packageId) {
745
+ const configContent = project.getSfProjectJson().getContents();
746
+ const packageAliases = configContent.packageAliases || {};
747
+ const aliasForPackageId = getPackageAliasesFromId(packageId, project);
748
+ let packageName;
749
+ if (!aliasForPackageId || aliasForPackageId.length === 0) {
750
+ const query = `SELECT Name FROM Package2 WHERE Id = '${packageId}'`;
751
+ packageName = await connection.tooling
752
+ .query(query)
753
+ .then((pkgQueryResult) => { var _a; return (_a = pkgQueryResult.records[0]) === null || _a === void 0 ? void 0 : _a.Name; });
754
+ }
755
+ else {
756
+ packageName = aliasForPackageId[0];
757
+ }
758
+ const packageAlias = branch
759
+ ? `${packageName}@${packageVersionNumber}-${branch}`
760
+ : `${packageName}@${packageVersionNumber}`;
761
+ packageAliases[packageAlias] = packageVersionId;
762
+ return { packageAliases };
763
+ }
764
+ /**
765
+ * Return true if the queryResult.records[0].Status is equal to one of the values in statuses.
766
+ *
767
+ * @param results to examine
768
+ * @param statuses array of statuses to look for
769
+ * @returns {boolean} if one of the values in status is found.
770
+ */
771
+ function _isStatusEqualTo(results, statuses) {
772
+ return (results === null || results === void 0 ? void 0 : results.length) <= 0 ? false : statuses === null || statuses === void 0 ? void 0 : statuses.some((status) => results[0].Status === status);
773
+ }
774
+ // added for unit testing
775
+ function getSoqlWhereClauseMaxLength() {
776
+ return exports.SOQL_WHERE_CLAUSE_MAX_LENGTH;
777
+ }
778
+ exports.getSoqlWhereClauseMaxLength = getSoqlWhereClauseMaxLength;
779
+ function formatDate(date) {
780
+ const pad = (num) => {
781
+ return num < 10 ? `0${num}` : num;
782
+ };
783
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
784
+ }
785
+ exports.formatDate = formatDate;
786
+ //# sourceMappingURL=packageUtils.js.map