From c3b1bfaf5754b03993876d0a456039fe414e0537 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Wed, 13 May 2026 12:57:16 -0400 Subject: [PATCH 01/14] Added extra metadata (used only in the pipeline & testing) to recreate previously reported issues related to quiddity & user session ID * Added an example email service, ExampleInboundEmailService, to have a full solution to re-test issue #155 * Added Experience Cloud metadata SSO logins, auth provider, and custom registration handlers, to have a full solution to re-test issue #224 --- .gitignore | 1 + config/scratch-orgs/advanced-scratch-def.json | 29 ++- .../ExampleGoogleAuth.authprovider-meta.xml | 13 ++ .../classes/ExampleRegistrationHandler.cls | 199 ++++++++++++++++++ .../ExampleRegistrationHandler.cls-meta.xml | 5 + .../ExampleRegistrationHandlerTest.cls | 85 ++++++++ ...xampleRegistrationHandlerTest.cls-meta.xml | 5 + .../Logger Test Aura Site.network-meta.xml | 31 ++- .../sites/Logger_Test_Aura_Site.site-meta.xml | 4 +- .../state-country-scratch-def.json | 34 +++ .../ExampleInboundEmailService.xml-meta.xml | 22 ++ package.json | 2 + scripts/dev/assign-role-to-current-user.js | 87 ++++++++ scripts/dev/link-community-auth-provider.js | 115 ++++++++++ scripts/dev/setup-dev-scratch-org.sh | 19 -- 15 files changed, 622 insertions(+), 29 deletions(-) create mode 100644 config/scratch-orgs/experience-cloud/authproviders/ExampleGoogleAuth.authprovider-meta.xml create mode 100644 config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls create mode 100644 config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls-meta.xml create mode 100644 config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls create mode 100644 config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls-meta.xml create mode 100644 config/scratch-orgs/state-country-scratch-def.json create mode 100644 nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml create mode 100644 scripts/dev/assign-role-to-current-user.js create mode 100644 scripts/dev/link-community-auth-provider.js delete mode 100644 scripts/dev/setup-dev-scratch-org.sh diff --git a/.gitignore b/.gitignore index 1119bd874..a02dbe5fa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ docs/apex/Miscellaneous/ temp/ test-coverage/ +.claude/ # NPM node_modules/ diff --git a/config/scratch-orgs/advanced-scratch-def.json b/config/scratch-orgs/advanced-scratch-def.json index 94b57a01d..620e28b20 100644 --- a/config/scratch-orgs/advanced-scratch-def.json +++ b/config/scratch-orgs/advanced-scratch-def.json @@ -2,9 +2,17 @@ "orgName": "Nebula Logger - Advanced Scratch Org", "edition": "Enterprise", "hasSampleData": true, - "country": "US", - "language": "en_US", - "features": ["Communities", "EinsteinGPTPlatform", "EventLogFile", "OmniStudioDesigner", "OmniStudioRuntime", "PlatformCache"], + "features": [ + "Communities", + "ContactsToMultipleAccounts", + "EinsteinGPTPlatform", + "EventLogFile", + "OmniStudioDesigner", + "OmniStudioRuntime", + "PersonAccounts", + "PlatformCache", + "StateAndCountryPicklist" + ], "settings": { "apexSettings": { "enableAuraApexCtrlAuthUserAccessCheckPref": true, @@ -18,6 +26,9 @@ "communitiesSettings": { "enableNetworksEnabled": true }, + "enhancedNotesSettings": { + "enableEnhancedNotes": true + }, "emailAdministrationSettings": { "enableEnhancedEmailEnabled": true }, @@ -37,11 +48,23 @@ "enableOmniStudioMetadata": true, "enableStandardOmniStudioRuntime": true }, + "nameSettings": { + "enableMiddleName": true, + "enableNameSuffix": true + }, "pathAssistantSettings": { "pathAssistantEnabled": true }, "platformEventSettings": { "enableEnhancedUsageMetrics": true + }, + "securitySettings": { + "enableAuditFieldsInactiveOwner": "true", + "sessionSettings": { + "lockSessionsToDomain": "false", + "lockSessionsToIp": "false", + "sessionTimeout": "TwelveHours" + } } } } diff --git a/config/scratch-orgs/experience-cloud/authproviders/ExampleGoogleAuth.authprovider-meta.xml b/config/scratch-orgs/experience-cloud/authproviders/ExampleGoogleAuth.authprovider-meta.xml new file mode 100644 index 000000000..83478ca15 --- /dev/null +++ b/config/scratch-orgs/experience-cloud/authproviders/ExampleGoogleAuth.authprovider-meta.xml @@ -0,0 +1,13 @@ + + + test-dzvrxqazkvaw@example.com + ExampleGoogleAuth + false + true + Google + ExampleRegistrationHandler + false + false + false + true + diff --git a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls new file mode 100644 index 000000000..7f9888089 --- /dev/null +++ b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls @@ -0,0 +1,199 @@ +global class ExampleRegistrationHandler implements Auth.RegistrationHandler { + private static final String LOGGER_SCENARIO = 'Nebula Logger - Experience Cloud testing'; + + global Schema.User createUser(Id portalId, Auth.UserData userData) { + Logger.setScenario(LOGGER_SCENARIO); + Logger.info( + new LogMessage('Running custom auth registration handler for portalId {0} with user data:\n\n{1}', portalId, System.JSON.serializePretty(userData)) + ); + Logger.saveLog(); + + try { + Schema.User existingUser = findExistingUser(userData); + if (existingUser != null) { + return existingUser; + } + + Schema.Contact contact = getOrCreateContact(userData); + Schema.Profile communityProfile = getCommunityProfile(); + + String email = normalizeEmail(userData.email); + String localPart = email != null ? email.substringBefore('@') : 'social.user'; + String timeSuffix = String.valueOf(DateTime.now().getTime()); + + Schema.User newUser = new Schema.User( + Alias = makeAlias(localPart), + CommunityNickname = makeNickname(localPart, timeSuffix), + ContactId = contact.Id, + Email = (email != null ? email : localPart + '+' + timeSuffix + '@example.invalid'), + EmailEncodingKey = 'UTF-8', + FederationIdentifier = userData.identifier, + FirstName = String.isBlank(userData.firstName) ? 'Social' : userData.firstName, + LanguageLocaleKey = 'en_US', + LastName = String.isBlank(userData.lastName) ? 'User' : userData.lastName, + LocaleSidKey = 'en_US', + ProfileId = communityProfile.Id, + TimeZoneSidKey = 'America/Los_Angeles', + Username = (email != null ? email : localPart + '.' + timeSuffix + '@example.invalid') + ); + + insert newUser; + + Logger.info('Successfully created new user', newUser); + Logger.saveLog(); + + return newUser; + } catch (System.Exception ex) { + Logger.exception('Error trying to create user', ex); + // Logger.exception (above) will thrown an exception, + // but to keep the Apex compiler happy, we have to have return null below + return null; + } + } + + global void updateUser(Id userId, Id portalId, Auth.UserData userData) { + Schema.User userToUpdate = new Schema.User( + Id = userId, + FirstName = String.isBlank(userData.firstName) ? null : userData.firstName, + LastName = String.isBlank(userData.lastName) ? null : userData.lastName, + Email = normalizeEmail(userData.email), + FederationIdentifier = userData.identifier + ); + update userToUpdate; + } + + private static Schema.User findExistingUser(Auth.UserData userData) { + if (String.isNotBlank(userData.identifier)) { + List usersByFederationId = [ + SELECT Id, FederationIdentifier + FROM User + WHERE FederationIdentifier = :userData.identifier + LIMIT 1 + ]; + if (!usersByFederationId.isEmpty()) { + return usersByFederationId[0]; + } + } + + String email = normalizeEmail(userData.email); + if (email != null) { + List usersByEmail = [ + SELECT Id, Email + FROM User + WHERE Email = :email + LIMIT 1 + ]; + if (!usersByEmail.isEmpty()) { + return usersByEmail[0]; + } + } + + return null; + } + + private static Schema.Contact getOrCreateContact(Auth.UserData userData) { + String email = normalizeEmail(userData.email); + if (email != null) { + List existingContacts = [ + SELECT Id + FROM Contact + WHERE Email = :email + LIMIT 1 + ]; + if (!existingContacts.isEmpty()) { + return existingContacts[0]; + } + } + + Schema.Account account = new Schema.Account(Name = 'Experience Cloud Social Login Users'); + insert account; + + Schema.Contact contact = new Schema.Contact( + AccountId = account.Id, + FirstName = String.isBlank(userData.firstName) ? 'Social' : userData.firstName, + LastName = String.isBlank(userData.lastName) ? 'User' : userData.lastName, + Email = email + ); + insert contact; + return contact; + } + + private static Schema.Profile getCommunityProfile() { + Id siteId = Site.getSiteId(); + if (siteId != null) { + Site currentSite = [SELECT GuestUserId FROM Site WHERE Id = :siteId LIMIT 1]; + if (currentSite.GuestUserId != null) { + Schema.User guestUser = [ + SELECT Profile.Name + FROM User + WHERE Id = :currentSite.GuestUserId + LIMIT 1 + ]; + + String siteNamePrefix = getSiteNamePrefixFromGuestProfileName(guestUser.Profile.Name); + if (String.isNotBlank(siteNamePrefix)) { + List siteScopedProfiles = [ + SELECT Id, Name, UserType + FROM Profile + WHERE UserType IN ('CSPLitePortal', 'PowerCustomerSuccess', 'CustomerSuccess') AND Name LIKE :siteNamePrefix + '%' + ORDER BY Name + LIMIT 1 + ]; + if (!siteScopedProfiles.isEmpty()) { + return siteScopedProfiles[0]; + } + } + } + } + + List fallbackByNameProfiles = [ + SELECT Id, Name, UserType + FROM Profile + WHERE UserType IN ('CSPLitePortal', 'PowerCustomerSuccess', 'CustomerSuccess') AND Name = 'Logger Test Site User Profile' + LIMIT 1 + ]; + if (!fallbackByNameProfiles.isEmpty()) { + return fallbackByNameProfiles[0]; + } + + List genericFallbackProfiles = [ + SELECT Id, Name, UserType + FROM Profile + WHERE UserType IN ('CSPLitePortal', 'PowerCustomerSuccess', 'CustomerSuccess') + ORDER BY Name + LIMIT 1 + ]; + if (genericFallbackProfiles.isEmpty()) { + throw new System.IllegalArgumentException('No Experience Cloud user profile found. Create/enable a community user profile first.'); + } + return genericFallbackProfiles[0]; + } + + private static String getSiteNamePrefixFromGuestProfileName(String guestProfileName) { + if (String.isBlank(guestProfileName)) { + return null; + } + String suffix = ' Guest Profile'; + if (guestProfileName.endsWith(suffix)) { + return guestProfileName.substring(0, guestProfileName.length() - suffix.length()); + } + return guestProfileName; + } + + private static String normalizeEmail(String email) { + return String.isBlank(email) ? null : email.trim().toLowerCase(); + } + + private static String makeAlias(String value) { + String cleaned = String.isBlank(value) ? 'socialusr' : value.replaceAll('[^A-Za-z0-9]', ''); + if (cleaned.length() < 2) { + cleaned = cleaned + 'usr'; + } + return cleaned.left(8); + } + + private static String makeNickname(String value, String suffix) { + String cleaned = String.isBlank(value) ? 'social.user' : value.replaceAll('[^A-Za-z0-9]', '.'); + return (cleaned.left(Math.min(20, cleaned.length())) + '.' + suffix.right(6)).toLowerCase(); + } +} diff --git a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls-meta.xml b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls-meta.xml new file mode 100644 index 000000000..cad713d0a --- /dev/null +++ b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandler.cls-meta.xml @@ -0,0 +1,5 @@ + + + 66.0 + Active + diff --git a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls new file mode 100644 index 000000000..ce62673ab --- /dev/null +++ b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls @@ -0,0 +1,85 @@ +@IsTest +private class ExampleRegistrationHandlerTest { + @IsTest + static void createUser_returnsExistingUser_whenFederationIdentifierMatches() { + Schema.User existingUser = createInternalUser('fed-match@example.com', 'fed-match-1'); + Auth.UserData userData = makeUserData('fed-match-1', 'Someone', 'Else', 'FED-MATCH@EXAMPLE.COM'); + + System.Test.startTest(); + Schema.User result = new ExampleRegistrationHandler().createUser(null, userData); + System.Test.stopTest(); + + System.assertEquals(existingUser.Id, result.Id, 'Expected existing user to be returned by federation identifier.'); + } + + @IsTest + static void createUser_returnsExistingUser_whenEmailMatches() { + Schema.User existingUser = createInternalUser('email-match@example.com', null); + Auth.UserData userData = makeUserData(null, 'Another', 'Person', 'EMAIL-MATCH@EXAMPLE.COM'); + + System.Test.startTest(); + Schema.User result = new ExampleRegistrationHandler().createUser(null, userData); + System.Test.stopTest(); + + System.assertEquals(existingUser.Id, result.Id, 'Expected existing user to be returned by email fallback.'); + } + + @IsTest + static void updateUser_updatesMappedFields() { + Schema.User existingUser = createInternalUser('update-me@example.com', null); + Auth.UserData updatedData = makeUserData('updated-federation-id', 'UpdatedFirst', 'UpdatedLast', 'updated@example.com'); + + System.Test.startTest(); + new ExampleRegistrationHandler().updateUser(existingUser.Id, null, updatedData); + System.Test.stopTest(); + + Schema.User actualUser = [ + SELECT Email, FirstName, LastName, FederationIdentifier + FROM User + WHERE Id = :existingUser.Id + ]; + + System.assertEquals('updated@example.com', actualUser.Email); + System.assertEquals('UpdatedFirst', actualUser.FirstName); + System.assertEquals('UpdatedLast', actualUser.LastName); + System.assertEquals('updated-federation-id', actualUser.FederationIdentifier); + } + + private static Auth.UserData makeUserData(String identifier, String firstName, String lastName, String email) { + Map attributeMap = new Map(); + return new Auth.UserData( + identifier, + firstName, + lastName, + firstName + ' ' + lastName, + email, + null, + email, + 'en_US', + 'Google', + null, + attributeMap + ); + } + + private static Schema.User createInternalUser(String email, String federationIdentifier) { + String timestamp = String.valueOf(DateTime.now().getTime()); + String username = 'example.handler.' + timestamp + '.' + Math.abs(Crypto.getRandomInteger()) + '@example.invalid'; + Id currentUserProfileId = [SELECT ProfileId FROM User WHERE Id = :System.UserInfo.getUserId()].ProfileId; + + Schema.User userToInsert = new Schema.User( + ProfileId = currentUserProfileId, + Username = username, + Email = email, + LastName = 'Test User', + Alias = 'tuser', + TimeZoneSidKey = 'America/Los_Angeles', + LocaleSidKey = 'en_US', + EmailEncodingKey = 'UTF-8', + LanguageLocaleKey = 'en_US', + FederationIdentifier = federationIdentifier + ); + insert userToInsert; + return userToInsert; + } +} diff --git a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls-meta.xml b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls-meta.xml new file mode 100644 index 000000000..cad713d0a --- /dev/null +++ b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 66.0 + Active + diff --git a/config/scratch-orgs/experience-cloud/networks/Logger Test Aura Site.network-meta.xml b/config/scratch-orgs/experience-cloud/networks/Logger Test Aura Site.network-meta.xml index 9ce021b30..029e67596 100644 --- a/config/scratch-orgs/experience-cloud/networks/Logger Test Aura Site.network-meta.xml +++ b/config/scratch-orgs/experience-cloud/networks/Logger Test Aura Site.network-meta.xml @@ -3,10 +3,11 @@ false false unfiled$public/CommunityChangePasswordEmailTemplate - + true - salesforce.nebula.packages@jongpie.com + salesforce.nebula.packages@jongpie.com.invalid Logger Test Aura Site + false true false true @@ -33,18 +34,38 @@ false unfiled$public/CommunityHeadlessForgotPasswordTemplate unfiled$public/CommunityHeadlessRegistrationTemplate + + false + true + false + false + false + false + false + false + false + false + false + false + false + false + false + 5 + 0.0 + LoggerLogCreator admin - Logger Test Site User Profile + logger test site user profile Standard - Designer + Standard Designer - Designer + Standard Designer + salesforce.nebula.packages@jongpie.com Logger_Test_Aura_Site1 false true diff --git a/config/scratch-orgs/experience-cloud/sites/Logger_Test_Aura_Site.site-meta.xml b/config/scratch-orgs/experience-cloud/sites/Logger_Test_Aura_Site.site-meta.xml index 9c5ed929d..5ed7f61ee 100644 --- a/config/scratch-orgs/experience-cloud/sites/Logger_Test_Aura_Site.site-meta.xml +++ b/config/scratch-orgs/experience-cloud/sites/Logger_Test_Aura_Site.site-meta.xml @@ -23,8 +23,8 @@ false true CommunitiesSelfReg - test-poxlhhmpqbuq@example.com - test-poxlhhmpqbuq@example.com + test-dzvrxqazkvaw@example.com + test-dzvrxqazkvaw@example.com ChatterNetwork loggeraura diff --git a/config/scratch-orgs/state-country-scratch-def.json b/config/scratch-orgs/state-country-scratch-def.json new file mode 100644 index 000000000..1430bbecd --- /dev/null +++ b/config/scratch-orgs/state-country-scratch-def.json @@ -0,0 +1,34 @@ +{ + "orgName": "nebula-logger-core", + "edition": "Enterprise", + "features": ["Communities", "ContactsToMultipleAccounts", "PersonAccounts", "StateAndCountryPicklist"], + "settings": { + "communitiesSettings": { + "enableNetworksEnabled": true + }, + "enhancedNotesSettings": { + "enableEnhancedNotes": true + }, + "emailAdministrationSettings": { + "enableEnhancedEmailEnabled": true + }, + "experienceBundleSettings": { + "enableExperienceBundleMetadata": true + }, + "lightningExperienceSettings": { + "enableS1DesktopEnabled": true + }, + "nameSettings": { + "enableMiddleName": true, + "enableNameSuffix": true + }, + "securitySettings": { + "enableAuditFieldsInactiveOwner": "true", + "sessionSettings": { + "lockSessionsToDomain": "false", + "lockSessionsToIp": "false", + "sessionTimeout": "TwelveHours" + } + } + } +} diff --git a/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml b/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml new file mode 100644 index 000000000..13c6ed79b --- /dev/null +++ b/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml @@ -0,0 +1,22 @@ + + + ExampleInboundEmailHandler + None + Discard + Discard + + salesforce.com, nebulalogger.com, jongpie.com + ExampleEmailAddress + true + exampleinboundemailservice + test-a9tdmyxmog79@example.com + + Discard + ExampleInboundEmailService + true + false + false + false + false + Discard + diff --git a/package.json b/package.json index 7c119af9b..8264a3ea0 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "docs:verify": "pwsh ./scripts/build/verify-docs-up-to-date.ps1", "husky:pre-commit": "lint-staged", "lwc:preview:recipes": "sf lightning dev app --device-type desktop --name \"Logger Recipes\"", + "scratch:assign:current-user-role": "node ./scripts/dev/assign-role-to-current-user.js", + "scratch:link:community:authprovider": "node ./scripts/dev/link-community-auth-provider.js --network \"Logger Test Aura Site\" --auth-provider ExampleGoogleAuth", "package:manifest:generate:core": "sf project generate manifest --source-dir ./nebula-logger/core/ --output-dir ./nebula-logger/ --name core.package", "package:version:create:managed": "pwsh ./scripts/build/create-managed-package-beta-version.ps1", "package:version:create:unlocked": "sf package version create --json --package \"Nebula Logger - Core\" --skip-ancestor-check --code-coverage --installation-key-bypass --wait 30", diff --git a/scripts/dev/assign-role-to-current-user.js b/scripts/dev/assign-role-to-current-user.js new file mode 100644 index 000000000..afc621e25 --- /dev/null +++ b/scripts/dev/assign-role-to-current-user.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +const { spawnSync } = require('child_process'); +const { parseArgs } = require('util'); + +const { values } = parseArgs({ + options: { + role: { type: 'string' }, + 'target-org': { type: 'string' } + } +}); + +const roleName = values.role || 'Scratch Org User Role'; +const targetOrg = values['target-org']; + +const isWindows = process.platform === 'win32'; + +function quoteForShell(arg) { + if (isWindows) { + return `"${String(arg).replace(/"/g, '\\"')}"`; + } + return `'${String(arg).replace(/'/g, `'\\''`)}'`; +} + +function runSf(args) { + const fullArgs = [...args, '--json']; + if (targetOrg) { + fullArgs.push('--target-org', targetOrg); + } + const command = ['sf', ...fullArgs.map(quoteForShell)].join(' '); + const result = spawnSync(command, { encoding: 'utf8', shell: true }); + if (result.error) { + console.error(result.error.message); + process.exit(1); + } + if (result.status !== 0) { + console.error(result.stderr || result.stdout); + process.exit(result.status ?? 1); + } + return JSON.parse(result.stdout); +} + +function escapeSoql(value) { + return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} + +function querySingle(soql) { + const response = runSf(['data', 'query', '--query', soql]); + return response.result.records?.[0]; +} + +const orgInfo = runSf(['org', 'display', '--verbose']); +const currentUsername = orgInfo.result.username; +if (!currentUsername) { + console.error('Could not resolve the current org username.'); + process.exit(1); +} + +const currentUser = querySingle(`SELECT Id, Username, UserRoleId FROM User WHERE Username = '${escapeSoql(currentUsername)}' LIMIT 1`); +if (!currentUser) { + console.error(`Could not find user with Username '${currentUsername}'.`); + process.exit(1); +} + +if (currentUser.UserRoleId) { + console.info('Current user already has a role.'); + console.info(`Username: ${currentUser.Username}`); + console.info(`UserRoleId: ${currentUser.UserRoleId}`); + process.exit(0); +} + +let role = querySingle(`SELECT Id, Name FROM UserRole WHERE Name = '${escapeSoql(roleName)}' LIMIT 1`); +let roleId; +if (role) { + roleId = role.Id; + console.info(`Using existing role: ${roleName} (${roleId})`); +} else { + const createResponse = runSf(['data', 'create', 'record', '--sobject', 'UserRole', '--values', `Name=${roleName}`]); + roleId = createResponse.result.id; + console.info(`Created role: ${roleName} (${roleId})`); +} + +runSf(['data', 'update', 'record', '--sobject', 'User', '--record-id', currentUser.Id, '--values', `UserRoleId=${roleId}`]); + +console.info('Assigned role to current user.'); +console.info(`Username: ${currentUser.Username}`); +console.info(`UserRoleId: ${roleId}`); diff --git a/scripts/dev/link-community-auth-provider.js b/scripts/dev/link-community-auth-provider.js new file mode 100644 index 000000000..6b7db95ea --- /dev/null +++ b/scripts/dev/link-community-auth-provider.js @@ -0,0 +1,115 @@ +#!/usr/bin/env node + +const { spawnSync } = require('child_process'); +const { parseArgs } = require('util'); + +const { values } = parseArgs({ + options: { + network: { type: 'string' }, + 'auth-provider': { type: 'string' }, + 'target-org': { type: 'string' } + } +}); + +if (!values.network || !values['auth-provider']) { + console.error( + 'Usage: node ./scripts/dev/link-community-auth-provider.js --network --auth-provider [--target-org ]' + ); + process.exit(1); +} + +const networkName = values.network; +const authProviderDeveloperName = values['auth-provider']; +const targetOrg = values['target-org']; + +const isWindows = process.platform === 'win32'; + +// Quote each arg for a shell command line. On Windows, cmd.exe needs +// double-quoted args with internal `"` escaped; POSIX shells need single quotes +// with embedded `'` closed and re-quoted. +function quoteForShell(arg) { + if (isWindows) { + return `"${String(arg).replace(/"/g, '\\"')}"`; + } + return `'${String(arg).replace(/'/g, `'\\''`)}'`; +} + +function runSf(args) { + const fullArgs = [...args, '--json']; + if (targetOrg) { + fullArgs.push('--target-org', targetOrg); + } + // Use a single shell command string (instead of [cmd, args, {shell: true}]) + // to (a) let Windows resolve the `sf.cmd` shim and (b) avoid Node's DEP0190 + // deprecation warning that fires for the array+shell combination. + const command = ['sf', ...fullArgs.map(quoteForShell)].join(' '); + const result = spawnSync(command, { encoding: 'utf8', shell: true }); + if (result.error) { + console.error(result.error.message); + process.exit(1); + } + if (result.status !== 0) { + console.error(result.stderr || result.stdout); + process.exit(result.status ?? 1); + } + return JSON.parse(result.stdout); +} + +// SOQL string literals only need single quotes and backslashes escaped. +function escapeSoql(value) { + return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} + +function querySingle(soql) { + const response = runSf(['data', 'query', '--query', soql]); + return response.result.records?.[0]; +} + +const network = querySingle( + `SELECT Id, Name, UrlPathPrefix FROM Network WHERE Name = '${escapeSoql(networkName)}' LIMIT 1` +); +if (!network || !network.UrlPathPrefix) { + console.error(`Could not find Network '${networkName}' or its UrlPathPrefix.`); + process.exit(1); +} + +const authProvider = querySingle( + `SELECT Id, DeveloperName FROM AuthProvider WHERE DeveloperName = '${escapeSoql(authProviderDeveloperName)}' LIMIT 1` +); +if (!authProvider) { + console.error(`Could not find AuthProvider with DeveloperName '${authProviderDeveloperName}'.`); + process.exit(1); +} + +const authConfig = querySingle( + `SELECT Id, DeveloperName, Url, AuthOptionsAuthProvider FROM AuthConfig WHERE Type = 'Community' AND Url LIKE '%/${escapeSoql(network.UrlPathPrefix)}' LIMIT 1` +); +if (!authConfig) { + console.error(`Could not find AuthConfig for Network '${networkName}' (urlPathPrefix '${network.UrlPathPrefix}').`); + process.exit(1); +} + +console.info(`Network: ${networkName} (${network.Id})`); +console.info(`AuthProvider: ${authProviderDeveloperName} (${authProvider.Id})`); +console.info(`AuthConfig: ${authConfig.Id}`); + +const existingLink = querySingle( + `SELECT Id FROM AuthConfigProviders WHERE AuthConfigId = '${authConfig.Id}' AND AuthProviderId = '${authProvider.Id}' LIMIT 1` +); + +if (existingLink) { + console.info(`Auth provider is already linked (AuthConfigProviders.Id=${existingLink.Id}).`); +} else { + runSf([ + 'data', + 'create', + 'record', + '--sobject', + 'AuthConfigProviders', + '--values', + `AuthConfigId=${authConfig.Id} AuthProviderId=${authProvider.Id}` + ]); + console.info('Linked auth provider to community login configuration.'); +} + +console.info('Done.'); diff --git a/scripts/dev/setup-dev-scratch-org.sh b/scripts/dev/setup-dev-scratch-org.sh deleted file mode 100644 index a56af157c..000000000 --- a/scripts/dev/setup-dev-scratch-org.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -SCRATCH_ORG_ALIAS="$1" - -sf org create scratch --definition-file ./config/scratch-orgs/dev-scratch-def.json --alias $SCRATCH_ORG_ALIAS --duration-days 30 --set-default --json -wait - -sf project deploy start -c -wait - -npm run permset:assign:all -wait - -sf apex run --file ./scripts/data/create-sample-log-entries.apex -wait - -# These 2 commands require https://github.com/jamessimone/sf-trace-plugin -sf apex trace -sf lightning debug From 56034ad2d2c6fffd51ee05423284bb28249b7f8c Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Mon, 1 Jun 2026 16:16:38 -0400 Subject: [PATCH 02/14] Changed Logger.cls to remove most of the usage of UserInfo.getSessionId() - it can't cause problems if we don't use it ^_^ Now the only place that the session ID is referenced is when using the REST save method. The user session ID is synchronously passed to the internal class RestApiSaver - and passing it like this will (intentionally) force a synchronous runtime exception when there isn't a session ID (instead of it silently failing async) --- .../main/logger-engine/classes/Logger.cls | 58 ++++--------------- 1 file changed, 10 insertions(+), 48 deletions(-) diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index 661834039..0fd105574 100644 --- a/nebula-logger/core/main/logger-engine/classes/Logger.cls +++ b/nebula-logger/core/main/logger-engine/classes/Logger.cls @@ -20,7 +20,7 @@ global with sharing class Logger { private static final List LOG_ENTRIES_BUFFER = new List(); private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.'; private static final String ORGANIZATION_DOMAIN_URL = System.URL.getOrgDomainUrl()?.toExternalForm(); - private static final String REQUEST_ID = System.Request.getCurrent().getRequestId(); + private static final String REQUEST_ID = System.Request.getCurrent()?.getRequestId(); private static final Map SAVE_METHOD_NAME_TO_SAVE_METHOD = new Map(); private static final Map TRANSACTION_FIELD_TO_VALUE = new Map(); private static final String TRANSACTION_ID = System.UUID.randomUUID().toString(); @@ -36,7 +36,7 @@ global with sharing class Logger { private static Integer saveLogCallCount = 0; private static Boolean suspendSaving = false; @TestVisible - private static System.Quiddity transactionQuiddity = loadTransactionQuiddity(); + private static System.Quiddity transactionQuiddity = System.Request.getCurrent()?.getQuiddity() ?? System.Quiddity.UNDEFINED; private static String transactionScenario; private static final List CLASSES_TO_IGNORE { @@ -86,26 +86,6 @@ global with sharing class Logger { set; } - @SuppressWarnings('PMD.AvoidUnauthorizedGetSessionIdInApex') - private static final String USER_SESSION_ID { - get { - if (USER_SESSION_ID == null) { - // TODO Spring '24 release - simplify this (and other lazy-loaded values) - // by switching to the fancy, new ?? null coalescing operator - try { - USER_SESSION_ID = System.UserInfo.getSessionId(); - } catch (Exception ex) { - USER_SESSION_ID = ''; - } - // If System.UserInfo.getSessionId() returns null, set to an empty string to - // avoid calling System.UserInfo.getSessionId() again - USER_SESSION_ID = USER_SESSION_ID ?? ''; - } - return USER_SESSION_ID; - } - set; - } - private static String transactionSaveMethodName { get { if (transactionSaveMethodName == null) { @@ -3755,13 +3735,7 @@ global with sharing class Logger { LoggerDataStore.getJobQueue().enqueueJob(new QueueableSaver(logEntryEvents)); } when REST { - // If the user doesn't have a session ID (e.g., site guest user), the REST API call will fail - // To avoid that, use the EventBus instead (even though REST was specified) - if (String.isBlank(USER_SESSION_ID)) { - saveLog(Logger.SaveMethod.EVENT_BUS); - } else { - new RestApiSaver().insertRecords(logEntryEvents); - } + new RestApiSaver(System.UserInfo.getSessionId()).insertRecords(logEntryEvents); } when SYNCHRONOUS_DML { LoggerTriggerableContext logEntryEventsAfterContext = new LoggerTriggerableContext( @@ -3932,18 +3906,6 @@ global with sharing class Logger { return isStartTimeValid && isEndTimeValid; } - private static System.Quiddity loadTransactionQuiddity() { - // An error can sometimes occur when calling System.Request.getCurrent(), such as when logging - // from an Auth Provider class (that implements Auth.RegistrationHandler). As a workaround, - // skip calling System.Request.getCurrent() if there is no user session. - // TODO: see if there is a better approach for this long term (or hopefully Salesforce fixes the gack error) - if (String.isNotBlank(USER_SESSION_ID)) { - return System.Request.getCurrent().getQuiddity(); - } else { - return null; - } - } - private static void setAsyncContext(AsyncContext asyncContext) { // Only set the async context the first time that a non-null value is provided // Previous versions of Nebula Logger would always set it, but that wasn't the @@ -3978,12 +3940,7 @@ global with sharing class Logger { String messageTemplate = 'Nebula Logger - Saving {0} log entries via {1}, save method is {2}. Logger.saveLog() has been called {3} in the current transaction.'; LogMessage savingLogMessage = new LogMessage( messageTemplate, - new List{ - LOG_ENTRIES_BUFFER.size(), - (getCurrentQuiddity() ?? System.Quiddity.UNDEFINED).name(), - saveMethodName, - saveLogCallCount + (saveLogCallCount == 1 ? ' time' : ' times') - } + new List{ LOG_ENTRIES_BUFFER.size(), getCurrentQuiddity(), saveMethodName, saveLogCallCount + (saveLogCallCount == 1 ? ' time' : ' times') } ); return savingLogMessage.getMessage(); @@ -4090,6 +4047,11 @@ global with sharing class Logger { private class RestApiSaver { private final String baseURL = ORGANIZATION_DOMAIN_URL + '/services/data/' + getOrganizationApiVersion(); private final String compositeEndpoint = '/composite/sobjects'; + private final String sessionId; + + public RestApiSaver(String sessionId) { + this.sessionId = sessionId; + } @SuppressWarnings('PMD.ApexSuggestUsingNamedCred') /** @@ -4099,7 +4061,7 @@ global with sharing class Logger { public void insertRecords(List records) { System.HttpRequest request = new System.HttpRequest(); request.setEndpoint(baseURL + compositeEndpoint); - request.setHeader('Authorization', 'Bearer ' + USER_SESSION_ID); + request.setHeader('Authorization', 'Bearer ' + this.sessionId); request.setHeader('Content-Type', 'application/json; charset=utf-8'); request.setMethod('POST'); From 8bcaa5c8449112999c345f775e0145149c9264d5 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Mon, 1 Jun 2026 13:56:33 -0400 Subject: [PATCH 03/14] Bumped package version number, name, and description --- README.md | 2 +- nebula-logger/core/main/logger-engine/classes/Logger.cls | 2 +- .../core/main/logger-engine/lwc/logger/loggerService.js | 2 +- package.json | 2 +- sfdx-project.json | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c4db31bfb..ffd1f3570 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations. -## Unlocked Package - v4.18.3 +## Unlocked Package - v4.18.4 [![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU) [![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU) diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index 0fd105574..c79bad487 100644 --- a/nebula-logger/core/main/logger-engine/classes/Logger.cls +++ b/nebula-logger/core/main/logger-engine/classes/Logger.cls @@ -15,7 +15,7 @@ global with sharing class Logger { // There's no reliable way to get the version number dynamically in Apex @TestVisible - private static final String CURRENT_VERSION_NUMBER = 'v4.18.3'; + private static final String CURRENT_VERSION_NUMBER = 'v4.18.4'; private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG; private static final List LOG_ENTRIES_BUFFER = new List(); private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.'; diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js index cb87dbb96..3b782083e 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js @@ -10,7 +10,7 @@ import LoggerServiceTaskQueue from './loggerServiceTaskQueue'; import getSettings from '@salesforce/apex/ComponentLogger.getSettings'; import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries'; -const CURRENT_VERSION_NUMBER = 'v4.18.3'; +const CURRENT_VERSION_NUMBER = 'v4.18.4'; const CONSOLE_OUTPUT_CONFIG = { messagePrefix: `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `, diff --git a/package.json b/package.json index 8264a3ea0..a575725e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nebula-logger", - "version": "4.18.3", + "version": "4.18.4", "description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.", "author": "Jonathan Gillespie", "license": "MIT", diff --git a/sfdx-project.json b/sfdx-project.json index 1ae67f066..68d7320c4 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -9,9 +9,9 @@ "path": "./nebula-logger/core", "definitionFile": "./config/scratch-orgs/base-scratch-def.json", "scopeProfiles": true, - "versionNumber": "4.18.3.NEXT", - "versionName": "PrismJS Loading Improvements & Error Handling", - "versionDescription": "Fixed some issues with loading PrismJS from LoggerStaticResoures by switching to minified versions + added error handling with fallback display when PrismJS cannot be properly loaded", + "versionNumber": "4.18.4.NEXT", + "versionName": "Bugfix: Removed most of the usage of UserInfo.getSessionId()", + "versionDescription": "Fixed some issues related to trying to use UserInfo.getSessionId() by just.... not using it (for the most part)", "postInstallUrl": "https://github.com/jongpie/NebulaLogger/wiki", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases", "unpackagedMetadata": { From d0cccc68f2567d2aef148e4a14143044e618b9b2 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 11:02:51 -0400 Subject: [PATCH 04/14] Changed build.yml to run all scratch orgs in parallel (instead of 3 at a time), thanks to @jamessimone and @mitchspano for getting the devhub limit increased! --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aaaba715c..255b6b82c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -347,7 +347,7 @@ jobs: experience-cloud-scratch-org-tests: environment: 'Experience Cloud Scratch Org' name: 'Run Experience Cloud Scratch Org Tests' - needs: [code-quality-tests, advanced-scratch-org-tests] + needs: [code-quality-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' @@ -415,7 +415,7 @@ jobs: omnistudio-scratch-org-tests: environment: 'OmniStudio Scratch Org' name: 'Run OmniStudio Scratch Org Tests' - needs: [code-quality-tests, base-scratch-org-tests] + needs: [code-quality-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' @@ -487,7 +487,7 @@ jobs: platform-cache-scratch-org-tests: environment: 'Platform Cache Scratch Org' name: 'Run Platform Cache Scratch Org Tests' - needs: [code-quality-tests, event-monitoring-scratch-org-tests] + needs: [code-quality-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' From f9c43367f4d3fe91dcc12fd105de823c56d44619 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 11:09:24 -0400 Subject: [PATCH 05/14] Added new state & country picklist scratch org to build.yml --- .github/workflows/build.yml | 68 ++++++++++++++++++- ...te-and-country-picklists-scratch-def.json} | 2 +- 2 files changed, 68 insertions(+), 2 deletions(-) rename config/scratch-orgs/{state-country-scratch-def.json => state-and-country-picklists-scratch-def.json} (92%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 255b6b82c..2c9a96fb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -549,6 +549,71 @@ jobs: run: npx sf org delete scratch --no-prompt if: ${{ always() }} + state-and-country-picklists-scratch-tests: + environment: 'State & Country Picklists Scratch Org' + name: 'Run State & Country Picklists Scratch Org Tests' + needs: [code-quality-tests] + runs-on: ubuntu-22.04 + steps: + - name: 'Checkout source code' + uses: actions/checkout@v4 + + - name: 'Restore node_modules cache' + id: cache-npm + uses: actions/cache@v4 + with: + path: node_modules + key: npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + npm-${{ env.cache-name }}- + npm- + + - name: 'Install npm dependencies' + if: steps.cache-npm.outputs.cache-hit != 'true' + run: npm ci + + - name: 'Authorize Dev Hub' + shell: bash + run: | + npx sf version + echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key + npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub + env: + DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} + DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} + DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + + - name: 'Create Scratch Org' + run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/state-and-country-picklists-scratch-def.json --wait 20 --set-default --json + + # To ensure that all of the Apex classes in the core directory have 75+ code coverage, + # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` + - name: 'Validate Core Source in Scratch Org' + run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests + + # Now that the core directory has been deployed & tests have passed, deploy all of the metadata + - name: 'Deploy All Source to Scratch Org' + run: npx sf project deploy start --source-dir ./nebula-logger/ + + - name: 'Assign Logger Admin Permission Set' + run: npm run permset:assign:admin + + # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. + # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. + # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). + # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of + # integration testing to ensure that everything works with or without an active session. + - name: 'Run Apex Tests Asynchronously' + run: npm run test:apex:nocoverage + + - name: 'Run Apex Tests Synchronously' + run: npm run test:apex:nocoverage -- --synchronous + + - name: 'Delete Scratch Org' + run: npx sf org delete scratch --no-prompt + if: ${{ always() }} + create-managed-package-beta: if: ${{ github.ref != 'refs/heads/main' }} @@ -749,7 +814,8 @@ jobs: event-monitoring-scratch-org-tests, experience-cloud-scratch-org-tests, omnistudio-scratch-org-tests, - platform-cache-scratch-org-tests + platform-cache-scratch-org-tests, + state-and-country-picklists-scratch-tests ] runs-on: ubuntu-22.04 steps: diff --git a/config/scratch-orgs/state-country-scratch-def.json b/config/scratch-orgs/state-and-country-picklists-scratch-def.json similarity index 92% rename from config/scratch-orgs/state-country-scratch-def.json rename to config/scratch-orgs/state-and-country-picklists-scratch-def.json index 1430bbecd..6d2bbd8f0 100644 --- a/config/scratch-orgs/state-country-scratch-def.json +++ b/config/scratch-orgs/state-and-country-picklists-scratch-def.json @@ -1,5 +1,5 @@ { - "orgName": "nebula-logger-core", + "orgName": "Nebula Logger - State & Country Picklists Scratch Org", "edition": "Enterprise", "features": ["Communities", "ContactsToMultipleAccounts", "PersonAccounts", "StateAndCountryPicklist"], "settings": { From b849c54028e1e2f6553e9d6bf3ebc924d2325219 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 14:20:52 -0400 Subject: [PATCH 06/14] Refactored build.yml pipeline to share setup steps to make maintenance much easier - Split common CI setup into two composite actions, .github/actions/setup-npm/action.yml and .github/actions/authorize-dev-hub/action.yml - Extracted the scratch-org test sections into a new reusable workflow at .github/workflows/test-scratch-org.yml, with parameters for definition file, OmniStudio install, optional extra metadata directory, and Codecov upload toggle - Converted the 7 near-identical scratch-org jobs in build.yml into a single matrix-driven caller of the reusable workflow. Adding a new scratch org is now one matrix row; downstream `needs:` lists shrink from 8/9 names to 3. - Pipeline scratch orgs are now create with `--alias pipeline-scratch-org` and a `--description` containing the GitHub run ID, so the in-job deletion can make sure it's targeting the correct scratch org - Scratch org delete step has been updated to use `continue-on-error: true` so a transient delete failure does not mask a real test failure. --- .github/actions/authorize-dev-hub/action.yml | 38 + .github/actions/setup-npm/action.yml | 22 + .github/workflows/build.yml | 725 +++---------------- .github/workflows/test-scratch-org.yml | 141 ++++ 4 files changed, 299 insertions(+), 627 deletions(-) create mode 100644 .github/actions/authorize-dev-hub/action.yml create mode 100644 .github/actions/setup-npm/action.yml create mode 100644 .github/workflows/test-scratch-org.yml diff --git a/.github/actions/authorize-dev-hub/action.yml b/.github/actions/authorize-dev-hub/action.yml new file mode 100644 index 000000000..0f4745396 --- /dev/null +++ b/.github/actions/authorize-dev-hub/action.yml @@ -0,0 +1,38 @@ +# Composite action that authorizes the Salesforce Dev Hub via JWT and sets +# it as the default. All inputs are required. +name: 'Authorize Dev Hub' +description: 'Authorize the Salesforce Dev Hub via JWT and set it as the default' + +inputs: + auth-url: + description: 'Dev Hub login URL' + required: true + bot-username: + description: 'Dev Hub bot username' + required: true + consumer-key: + description: 'Dev Hub connected app consumer key' + required: true + jwt-server-key: + description: 'Dev Hub JWT private key (PEM)' + required: true + +runs: + using: 'composite' + steps: + - name: 'Authorize Dev Hub' + shell: bash + env: + DEV_HUB_AUTH_URL: ${{ inputs.auth-url }} + DEV_HUB_BOT_USERNAME: ${{ inputs.bot-username }} + DEV_HUB_CONSUMER_KEY: ${{ inputs.consumer-key }} + DEV_HUB_JWT_SERVER_KEY: ${{ inputs.jwt-server-key }} + run: | + npx sf version + echo "$DEV_HUB_JWT_SERVER_KEY" > ./jwt-server.key + npx sf org login jwt --instance-url "$DEV_HUB_AUTH_URL" --client-id "$DEV_HUB_CONSUMER_KEY" --username "$DEV_HUB_BOT_USERNAME" --jwt-key-file ./jwt-server.key --set-default-dev-hub + + - name: 'Delete JWT key file' + if: ${{ always() }} + shell: bash + run: rm -f ./jwt-server.key diff --git a/.github/actions/setup-npm/action.yml b/.github/actions/setup-npm/action.yml new file mode 100644 index 000000000..0a805b713 --- /dev/null +++ b/.github/actions/setup-npm/action.yml @@ -0,0 +1,22 @@ +# Composite action that restores the node_modules cache and installs +# npm dependencies on a cache miss. Used by every job in build.yml. +name: 'Setup npm' +description: 'Restore the node_modules cache and run npm ci on a cache miss' + +runs: + using: 'composite' + steps: + - name: 'Restore node_modules cache' + id: cache-npm + uses: actions/cache@v4 + with: + path: node_modules + key: npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + npm-${{ env.cache-name }}- + npm- + + - name: 'Install npm dependencies' + if: steps.cache-npm.outputs.cache-hit != 'true' + shell: bash + run: npm ci diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c9a96fb9..eb646b657 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,7 @@ on: - main paths: - .github/workflows/** + - .github/actions/** - config/scratch-orgs/** - nebula-logger/** - sfdx-project.json @@ -19,6 +20,7 @@ on: types: [opened, synchronize, reopened] paths: - .github/workflows/** + - .github/actions/** - config/scratch-orgs/** - nebula-logger/** - sfdx-project.json @@ -31,20 +33,6 @@ jobs: - name: 'Checkout source code' uses: actions/checkout@v4 - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - name: 'Check for changes in core directory' uses: dorny/paths-filter@v3 id: changes @@ -53,18 +41,17 @@ jobs: core: - './nebula-logger/core/**' + - name: 'Setup npm' + uses: ./.github/actions/setup-npm + - name: 'Authorize Dev Hub' if: ${{ (github.event_name == 'pull_request') && (github.event.pull_request.draft == false) && (steps.changes.outputs.core == 'true') }} - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - name: 'Verify package version number is updated' if: ${{ (github.event_name == 'pull_request') && (github.event.pull_request.draft == false) && (steps.changes.outputs.core == 'true') }} @@ -74,8 +61,7 @@ jobs: run: npm run scan:lwc - name: 'Verify Apex with Code Analyzer' - run: | - npm run scan:apex + run: npm run scan:apex # TODO - uncomment - temporarily commented-out due to an issue with apexdocs in the pipeline # - name: 'Verify docs are updated' @@ -92,19 +78,8 @@ jobs: - name: 'Checkout source code' uses: actions/checkout@v4 - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci + - name: 'Setup npm' + uses: ./.github/actions/setup-npm - name: 'Run LWC Tests' run: npm run test:lwc @@ -116,520 +91,72 @@ jobs: flags: LWC token: ${{ secrets.CODECOV_TOKEN }} - advanced-scratch-org-tests: - environment: 'Advanced Scratch Org' - name: 'Run Advanced Scratch Org Tests' + # Each matrix row spawns an independent run of the scratch-org-tests reusable workflow, + # all in parallel. To add a new scratch org, add a new row - no other edits needed. + # + # The Base row is the canonical environment for Codecov code coverage reporting: + # only the `LoggerCore` test suite's results are uploaded, which validates that the core + # metadata alone (what teams deploy when they cannot install one of Nebula Logger's 2GP + # packages) has sufficient coverage to deploy to a prod org. Orgs without optional + # Salesforce features (Experience Cloud, OmniStudio, Platform Cache, etc.) tend to report + # lower coverage, so the base scratch org acts as the most conservative baseline. + scratch-org-tests: + name: 'Run ${{ matrix.label }} Scratch Org Tests' needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/advanced-scratch-def.json --wait 20 --set-default --json - - # https://help.salesforce.com/s/articleView?id=000394906&type=1 - - name: "Install OmniStudio managed package v258.6 (Winter '26 release)" - run: npx sf package install --package 04tKb000000tAtyIAE --security-type AdminsOnly --wait 30 --no-prompt - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Deploy Test Metadata' - run: npx sf project deploy start --source-dir ./config/scratch-orgs/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - base-scratch-org-tests: - environment: 'Base Scratch Org' - name: 'Run Base Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/base-scratch-def.json --wait 20 --set-default --json - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - # This is the only place in Nebula Logger's pipeline where the `LoggerCore` test suite runs & the results are sent to Codecov.io. - # This is specifically done in the base scratch org, using only the `LoggerCore test` suite, in order to help validate that the core metadata - # provides sufficient code coverage for teams that deploy Nebula Logger's metadata directly to their org, instead of installing one of Nebula Logger's 2GP packages. - # 1. Many teams cannot use one of the 2GP packages for various reasons, including company policies, security concerns, etc., - # and being able to deploy the core metadata is something that should be supported. - # 2. And even though the pipeline runs the `extra-tests` directory to validate logging works correctly in various types of scratch orgs, - # for teams that are trying to deploy Nebula Logger's metadata, it's critical that the core tests have sufficient code coverage to be deployed to a prod org. - # - In the past, this has not always been the case, resulting in some teams having deployment issues & project delays due to the low code coverage - # that was (formerly) provided by some of the core tests classes. - # - In orgs that do not have some optional Salesforce features enabled/available (e.g., orgs without Experience Cloud, OmniStudio, Platform Cache, etc.), - # the code coverage can be especially low, so using the base scratch org acts - # - - # So now only the core test suite's results, from a base scratch org, are used for reporting code coverage, even though the project's overall code coverage - # is much higher when using the `extra-tests` directory. - - name: 'Get Core Test Suite Code Coverage' - run: npm run test:apex:suite:core - - # This is the only scratch org that's used for uploading code coverage - - name: 'Upload Apex test code coverage to Codecov.io' - uses: codecov/codecov-action@v4 - with: - fail_ci_if_error: true - flags: Apex - token: ${{ secrets.CODECOV_TOKEN }} - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - event-monitoring-scratch-org-tests: - environment: 'Event Monitoring Scratch Org' - name: 'Run Event Monitoring Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/event-monitoring-scratch-def.json --wait 20 --set-default --json - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - experience-cloud-scratch-org-tests: - environment: 'Experience Cloud Scratch Org' - name: 'Run Experience Cloud Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/experience-cloud-scratch-def.json --wait 20 --set-default --json - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Deploy Test Experience Sites Metadata' - run: npx sf project deploy start --source-dir ./config/scratch-orgs/experience-cloud/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - omnistudio-scratch-org-tests: - environment: 'OmniStudio Scratch Org' - name: 'Run OmniStudio Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/omnistudio-scratch-def.json --wait 20 --set-default --json - - # https://help.salesforce.com/s/articleView?id=000394906&type=1 - - name: "Install OmniStudio managed package v258.6 (Winter '26 release)" - run: npx sf package install --package 04tKb000000tAtyIAE --security-type AdminsOnly --wait 30 --no-prompt - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Deploy Test OmniStudio Metadata' - run: npx sf project deploy start --source-dir ./config/scratch-orgs/omnistudio/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Base Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - platform-cache-scratch-org-tests: - environment: 'Platform Cache Scratch Org' - name: 'Run Platform Cache Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/platform-cache-scratch-def.json --wait 20 --set-default --json - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} - - state-and-country-picklists-scratch-tests: - environment: 'State & Country Picklists Scratch Org' - name: 'Run State & Country Picklists Scratch Org Tests' - needs: [code-quality-tests] - runs-on: ubuntu-22.04 - steps: - - name: 'Checkout source code' - uses: actions/checkout@v4 - - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci - - - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - - - name: 'Create Scratch Org' - run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file ./config/scratch-orgs/state-and-country-picklists-scratch-def.json --wait 20 --set-default --json - - # To ensure that all of the Apex classes in the core directory have 75+ code coverage, - # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` - - name: 'Validate Core Source in Scratch Org' - run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests - - # Now that the core directory has been deployed & tests have passed, deploy all of the metadata - - name: 'Deploy All Source to Scratch Org' - run: npx sf project deploy start --source-dir ./nebula-logger/ - - - name: 'Assign Logger Admin Permission Set' - run: npm run permset:assign:admin - - # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. - # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. - # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). - # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of - # integration testing to ensure that everything works with or without an active session. - - name: 'Run Apex Tests Asynchronously' - run: npm run test:apex:nocoverage - - - name: 'Run Apex Tests Synchronously' - run: npm run test:apex:nocoverage -- --synchronous - - - name: 'Delete Scratch Org' - run: npx sf org delete scratch --no-prompt - if: ${{ always() }} + strategy: + fail-fast: false + matrix: + include: + - label: 'Advanced' + environment: 'Advanced Scratch Org' + definition-file: ./config/scratch-orgs/advanced-scratch-def.json + install-omnistudio: true + extra-metadata-directory: ./config/scratch-orgs/ + + - label: 'Base' + environment: 'Base Scratch Org' + definition-file: ./config/scratch-orgs/base-scratch-def.json + run-core-test-suite: true + upload-apex-coverage: true + + - label: 'Event Monitoring' + environment: 'Event Monitoring Scratch Org' + definition-file: ./config/scratch-orgs/event-monitoring-scratch-def.json + + - label: 'Experience Cloud' + environment: 'Experience Cloud Scratch Org' + definition-file: ./config/scratch-orgs/experience-cloud-scratch-def.json + extra-metadata-directory: ./config/scratch-orgs/experience-cloud/ + + - label: 'OmniStudio' + environment: 'OmniStudio Scratch Org' + definition-file: ./config/scratch-orgs/omnistudio-scratch-def.json + install-omnistudio: true + extra-metadata-directory: ./config/scratch-orgs/omnistudio/ + + - label: 'Platform Cache' + environment: 'Platform Cache Scratch Org' + definition-file: ./config/scratch-orgs/platform-cache-scratch-def.json + + - label: 'State & Country Picklists' + environment: 'State & Country Picklists Scratch Org' + definition-file: ./config/scratch-orgs/state-and-country-picklists-scratch-def.json + uses: ./.github/workflows/test-scratch-org.yml + with: + environment: ${{ matrix.environment }} + definition-file: ${{ matrix.definition-file }} + install-omnistudio: ${{ matrix.install-omnistudio || false }} + extra-metadata-directory: ${{ matrix.extra-metadata-directory || '' }} + run-core-test-suite: ${{ matrix.run-core-test-suite || false }} + upload-apex-coverage: ${{ matrix.upload-apex-coverage || false }} + secrets: inherit create-managed-package-beta: if: ${{ github.ref != 'refs/heads/main' }} environment: 'Base Scratch Org' name: 'Create Managed Package Beta' - needs: - [ - code-quality-tests, - lwc-jest-tests, - advanced-scratch-org-tests, - base-scratch-org-tests, - event-monitoring-scratch-org-tests, - experience-cloud-scratch-org-tests, - omnistudio-scratch-org-tests, - platform-cache-scratch-org-tests - ] + needs: [code-quality-tests, lwc-jest-tests, scratch-org-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' @@ -637,31 +164,16 @@ jobs: with: ref: ${{ github.event.pull_request.head.ref }} - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci + - name: 'Setup npm' + uses: ./.github/actions/setup-npm - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - name: 'Create Beta Managed Package Version' run: npm run package:version:create:managed @@ -679,31 +191,16 @@ jobs: with: ref: ${{ github.event.pull_request.head.ref }} - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci + - name: 'Setup npm' + uses: ./.github/actions/setup-npm - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - name: 'Create Scratch Org' run: npx sf org create scratch --no-namespace --no-track-source --alias base_package_subscriber_scratch_org --duration-days 1 --definition-file ./config/scratch-orgs/base-scratch-def.json --wait 20 --set-default --json @@ -805,48 +302,22 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} name: 'Promote Package Versions' - needs: - [ - code-quality-tests, - lwc-jest-tests, - advanced-scratch-org-tests, - base-scratch-org-tests, - event-monitoring-scratch-org-tests, - experience-cloud-scratch-org-tests, - omnistudio-scratch-org-tests, - platform-cache-scratch-org-tests, - state-and-country-picklists-scratch-tests - ] + needs: [code-quality-tests, lwc-jest-tests, scratch-org-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' uses: actions/checkout@v4 - - name: 'Restore node_modules cache' - id: cache-npm - uses: actions/cache@v4 - with: - path: node_modules - key: npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - npm-${{ env.cache-name }}- - npm- - - - name: 'Install npm dependencies' - if: steps.cache-npm.outputs.cache-hit != 'true' - run: npm ci + - name: 'Setup npm' + uses: ./.github/actions/setup-npm - name: 'Authorize Dev Hub' - shell: bash - run: | - npx sf version - echo "${{ env.DEV_HUB_JWT_SERVER_KEY }}" > ./jwt-server.key - npx sf org login jwt --instance-url ${{ env.DEV_HUB_AUTH_URL }} --client-id ${{ env.DEV_HUB_CONSUMER_KEY }} --username ${{ env.DEV_HUB_BOT_USERNAME }} --jwt-key-file ./jwt-server.key --set-default-dev-hub - env: - DEV_HUB_AUTH_URL: ${{ secrets.DEV_HUB_AUTH_URL }} - DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} - DEV_HUB_CONSUMER_KEY: ${{ secrets.DEV_HUB_CONSUMER_KEY }} - DEV_HUB_JWT_SERVER_KEY: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} - name: 'Promote package versions' run: npx pwsh ./scripts/build/promote-readme-packages.ps1 diff --git a/.github/workflows/test-scratch-org.yml b/.github/workflows/test-scratch-org.yml new file mode 100644 index 000000000..a3dcdf1c1 --- /dev/null +++ b/.github/workflows/test-scratch-org.yml @@ -0,0 +1,141 @@ +# Reusable workflow that creates a scratch org, deploys Nebula Logger, runs the +# Apex tests sync + async, and tears the scratch org down. Variations across the +# different scratch org definitions are controlled via inputs. +name: Test Scratch Org + +on: + workflow_call: + inputs: + environment: + description: 'GitHub environment used for secret scoping (e.g. "Base Scratch Org").' + required: true + type: string + definition-file: + description: 'Path to the scratch org definition file passed to `sf org create scratch`.' + required: true + type: string + install-omnistudio: + description: 'When true, install the OmniStudio managed package after creating the scratch org.' + required: false + type: boolean + default: false + extra-metadata-directory: + description: 'Optional source directory to deploy after the main metadata deploy. Empty string skips this step.' + required: false + type: string + default: '' + run-core-test-suite: + description: 'When true, run the LoggerCore Apex test suite for code coverage reporting.' + required: false + type: boolean + default: false + upload-apex-coverage: + description: 'When true, upload Apex test coverage to Codecov.io. Requires `run-core-test-suite: true`.' + required: false + type: boolean + default: false + secrets: + DEV_HUB_AUTH_URL: + required: true + DEV_HUB_BOT_USERNAME: + required: true + DEV_HUB_CONSUMER_KEY: + required: true + DEV_HUB_JWT_SERVER_KEY: + required: true + CODECOV_TOKEN: + required: false + +env: + SF_DISABLE_AUTOUPDATE: true + SF_LOG_LEVEL: debug + SF_SKIP_NEW_VERSION_CHECK: true + +jobs: + scratch-org-tests: + environment: ${{ inputs.environment }} + runs-on: ubuntu-22.04 + steps: + - name: 'Checkout source code' + uses: actions/checkout@v4 + + - name: 'Setup npm' + uses: ./.github/actions/setup-npm + + - name: 'Authorize Dev Hub' + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + + # Pipeline scratch orgs are tagged with the alias `pipeline-scratch-org` and a description + # that includes the GitHub run ID. The alias makes the delete step targeted (so it cannot + # accidentally delete a different default org), and the description marker is what the + # scheduled janitor workflow (.github/workflows/scratch-org-janitor.yml) uses to identify + # orphaned scratch orgs that escaped the in-job cleanup. + - name: 'Create Scratch Org' + env: + DEFINITION_FILE: ${{ inputs.definition-file }} + SCRATCH_ORG_DESCRIPTION: 'Nebula Logger pipeline run ${{ github.run_id }} attempt ${{ github.run_attempt }}' + run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file "$DEFINITION_FILE" --description "$SCRATCH_ORG_DESCRIPTION" --alias pipeline-scratch-org --wait 20 --set-default --json + + # https://help.salesforce.com/s/articleView?id=000394906&type=1 + - name: "Install OmniStudio managed package v258.6 (Winter '26 release)" + if: ${{ inputs.install-omnistudio }} + run: npx sf package install --package 04tKb000000tAtyIAE --security-type AdminsOnly --wait 30 --no-prompt + + # To ensure that all of the Apex classes in the core directory have 75+ code coverage, + # deploy only the core directory & run all of its tests as part of the deployment, using `--test-level RunLocalTests` + - name: 'Validate Core Source in Scratch Org' + run: npx sf project deploy validate --concise --source-dir ./nebula-logger/core/ --test-level RunLocalTests + + # Now that the core directory has been deployed & tests have passed, deploy all of the metadata + - name: 'Deploy All Source to Scratch Org' + run: npx sf project deploy start --source-dir ./nebula-logger/ + + - name: 'Deploy Extra Metadata' + if: ${{ inputs.extra-metadata-directory != '' }} + env: + EXTRA_METADATA_DIRECTORY: ${{ inputs.extra-metadata-directory }} + run: npx sf project deploy start --source-dir "$EXTRA_METADATA_DIRECTORY" + + - name: 'Assign Logger Admin Permission Set' + run: npm run permset:assign:admin + + # Nebula Logger has functionality that queries the AuthSession object when the current user has an active session. + # The code should work with or without an active session, so the pipeline runs the tests twice - asynchronously and synchronously. + # This is done because, based on how you execute Apex tests, the running user may have an active session (synchrously) or not (asynchronously). + # This data is also mocked during tests, but running the Apex tests sync & async serves within the pipeline acts as an extra level of + # integration testing to ensure that everything works with or without an active session. + - name: 'Run Apex Tests Asynchronously' + run: npm run test:apex:nocoverage + + - name: 'Run Apex Tests Synchronously' + run: npm run test:apex:nocoverage -- --synchronous + + # This step only runs in the base scratch org. The `LoggerCore` test suite's results are sent to Codecov.io + # specifically from a base scratch org so the reported code coverage reflects the metadata that teams who + # cannot install the 2GP packages will deploy directly. See the long-form rationale in the prior history of build.yml. + - name: 'Get Core Test Suite Code Coverage' + if: ${{ inputs.run-core-test-suite }} + run: npm run test:apex:suite:core + + - name: 'Upload Apex test code coverage to Codecov.io' + if: ${{ inputs.upload-apex-coverage }} + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + flags: Apex + token: ${{ secrets.CODECOV_TOKEN }} + + # `if: always()` runs this on success, failure, and most cancellations. It cannot run if + # the runner VM dies or if the cancellation grace period expires mid-step - the scheduled + # janitor workflow is the safety net for those cases. + # `continue-on-error: true` means a transient delete failure (rate-limit, API blip) does + # not mask a real test failure earlier in the job. The janitor will mop up if it happens. + - name: 'Delete Scratch Org' + if: ${{ always() }} + continue-on-error: true + run: npx sf org delete scratch --target-org pipeline-scratch-org --no-prompt From a157fddd87b3ab39215dd260df035c62abdf6997 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 14:21:52 -0400 Subject: [PATCH 07/14] Added new workflow (.github/workflows/delete-stale-scratch-orgs.yml) to run an hourly cleanup of orphaned pipeline scratch orgs older than 2 hours * For each scratch org, it parses the run ID out of the description and asks the GitHub REST API whether the owning run is still active. Orgs whose run is queued or in-progress are skipped, so a slow but healthy build never has its scratch org deleted while the job is still running. --- .../workflows/delete-stale-scratch-orgs.yml | 143 ++++++++++++++++++ .github/workflows/test-scratch-org.yml | 8 +- 2 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/delete-stale-scratch-orgs.yml diff --git a/.github/workflows/delete-stale-scratch-orgs.yml b/.github/workflows/delete-stale-scratch-orgs.yml new file mode 100644 index 000000000..da7d4c370 --- /dev/null +++ b/.github/workflows/delete-stale-scratch-orgs.yml @@ -0,0 +1,143 @@ +# Scheduled cleanup for orphaned pipeline scratch orgs. +# +# The build pipeline creates scratch orgs tagged with description +# "Nebula Logger pipeline run attempt " and tries to delete them +# in an `if: always()` step at the end of the test job. That cleanup is best-effort +# and cannot run when: +# 1. The runner VM dies (infra incidents, network partitions, hitting the 6h timeout). +# 2. A pull-request push auto-cancels a run, and the cancellation grace period expires +# while a slow step is still running, so the runner is killed before `if: always()` +# can fire. +# 3. The `sf org delete scratch` call itself errors transiently (rate-limit, API blip). +# +# This workflow is the safety net. It runs hourly, queries the Dev Hub for +# `ActiveScratchOrg` records tagged with the pipeline description marker that are +# older than 2 hours, parses the GitHub run ID out of each description, and asks the +# GitHub REST API for the run's status. Orgs whose owning run is still queued or +# in-progress are skipped so a slow but healthy build never has its scratch org +# yanked out from under it. Everything else is deleted directly via the Tooling API. +name: Delete Stale Scratch Orgs + +on: + schedule: + # Hourly. The 2h age filter + per-org in-progress check is what prevents this from + # killing healthy in-flight runs, not the cron interval. + - cron: '0 * * * *' + workflow_dispatch: + +env: + SF_DISABLE_AUTOUPDATE: true + SF_LOG_LEVEL: debug + SF_SKIP_NEW_VERSION_CHECK: true + +# Don't run more than one cleanup at a time. If a manual dispatch overlaps with the +# hourly cron, the second invocation queues until the first finishes. +concurrency: + group: delete-stale-scratch-orgs + cancel-in-progress: false + +# Need read access to other workflow runs so we can ask "is run still in-progress". +permissions: + actions: read + contents: read + +jobs: + delete-orphaned-scratch-orgs: + name: 'Delete Orphaned Pipeline Scratch Orgs' + environment: 'Base Scratch Org' + runs-on: ubuntu-22.04 + steps: + - name: 'Checkout source code' + uses: actions/checkout@v4 + + - name: 'Setup npm' + uses: ./.github/actions/setup-npm + + - name: 'Authorize Dev Hub' + uses: ./.github/actions/authorize-dev-hub + with: + auth-url: ${{ secrets.DEV_HUB_AUTH_URL }} + bot-username: ${{ secrets.DEV_HUB_BOT_USERNAME }} + consumer-key: ${{ secrets.DEV_HUB_CONSUMER_KEY }} + jwt-server-key: ${{ secrets.DEV_HUB_JWT_SERVER_KEY }} + + - name: 'Delete orphaned pipeline scratch orgs' + env: + DEV_HUB_BOT_USERNAME: ${{ secrets.DEV_HUB_BOT_USERNAME }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + + # ActiveScratchOrg holds the live scratch org records on the Dev Hub. Deleting an + # ActiveScratchOrg row releases the scratch org - same mechanism used by + # `sf org delete scratch`, but it doesn't require re-authenticating into the + # scratch org from this runner (we may not have the auth state any more). + QUERY="SELECT Id, SignupUsername, Description, CreatedDate FROM ActiveScratchOrg WHERE Description LIKE 'Nebula Logger pipeline run%' AND CreatedDate < LAST_N_HOURS:2" + + echo "Querying Dev Hub for pipeline scratch orgs older than 2h..." + query_result=$(npx sf data query --query "$QUERY" --target-org "$DEV_HUB_BOT_USERNAME" --json) + + total=$(echo "$query_result" | jq -r '.result.totalSize') + echo "Found $total candidate scratch org(s)." + + if [ "$total" = "0" ]; then + echo "Nothing to clean up." + exit 0 + fi + + echo "$query_result" | jq -r '.result.records[] | "\(.Id)\t\(.SignupUsername)\t\(.CreatedDate)\t\(.Description)"' + + deleted_count=0 + skipped_count=0 + failed_count=0 + + # Iterate as TSV so spaces in Description don't break the loop. + while IFS=$'\t' read -r record_id description; do + # Description format: "Nebula Logger pipeline run attempt " + run_id=$(echo "$description" | sed -nE 's/^Nebula Logger pipeline run ([0-9]+) attempt [0-9]+.*$/\1/p') + + if [ -z "$run_id" ]; then + echo "" + echo "ActiveScratchOrg $record_id has unparseable description: '$description'. Treating as orphaned." + run_status="unknown" + else + echo "" + echo "ActiveScratchOrg $record_id - checking GitHub run $run_id..." + # Pull just the status. If the run ID no longer exists (deleted, or wrong repo), + # gh exits non-zero and we treat that as "safe to delete". + if run_status=$(gh run view "$run_id" --repo "$GH_REPO" --json status --jq '.status' 2>/dev/null); then + echo " GitHub run $run_id status: $run_status" + else + echo " GitHub run $run_id not found (or inaccessible). Treating as orphaned." + run_status="not_found" + fi + fi + + # Statuses where the run is still alive: queued, in_progress, requested, waiting, pending. + # `completed` (with any conclusion) means the run is done and any leftover org is orphaned. + case "$run_status" in + queued|in_progress|requested|waiting|pending) + echo " Skipping ActiveScratchOrg $record_id - owning run is still active." + skipped_count=$((skipped_count + 1)) + ;; + *) + echo " Deleting ActiveScratchOrg $record_id..." + if npx sf data delete record --sobject ActiveScratchOrg --record-id "$record_id" --target-org "$DEV_HUB_BOT_USERNAME"; then + deleted_count=$((deleted_count + 1)) + else + echo " Failed to delete ActiveScratchOrg $record_id. Continuing with the rest." + failed_count=$((failed_count + 1)) + fi + ;; + esac + done < <(echo "$query_result" | jq -r '.result.records[] | "\(.Id)\t\(.Description)"') + + echo "" + echo "Cleanup summary: deleted=$deleted_count, skipped=$skipped_count, failed=$failed_count, total=$total" + + if [ "$failed_count" -gt 0 ]; then + echo "::warning::$failed_count orphaned scratch org(s) could not be deleted - investigate Dev Hub state." + exit 1 + fi diff --git a/.github/workflows/test-scratch-org.yml b/.github/workflows/test-scratch-org.yml index a3dcdf1c1..01e5a4647 100644 --- a/.github/workflows/test-scratch-org.yml +++ b/.github/workflows/test-scratch-org.yml @@ -73,8 +73,8 @@ jobs: # Pipeline scratch orgs are tagged with the alias `pipeline-scratch-org` and a description # that includes the GitHub run ID. The alias makes the delete step targeted (so it cannot # accidentally delete a different default org), and the description marker is what the - # scheduled janitor workflow (.github/workflows/scratch-org-janitor.yml) uses to identify - # orphaned scratch orgs that escaped the in-job cleanup. + # scheduled cleanup workflow (.github/workflows/delete-stale-scratch-orgs.yml) uses to + # identify orphaned scratch orgs that escaped the in-job cleanup. - name: 'Create Scratch Org' env: DEFINITION_FILE: ${{ inputs.definition-file }} @@ -132,9 +132,9 @@ jobs: # `if: always()` runs this on success, failure, and most cancellations. It cannot run if # the runner VM dies or if the cancellation grace period expires mid-step - the scheduled - # janitor workflow is the safety net for those cases. + # cleanup workflow (delete-stale-scratch-orgs.yml) is the safety net for those cases. # `continue-on-error: true` means a transient delete failure (rate-limit, API blip) does - # not mask a real test failure earlier in the job. The janitor will mop up if it happens. + # not mask a real test failure earlier in the job. The cleanup workflow will mop up if it happens. - name: 'Delete Scratch Org' if: ${{ always() }} continue-on-error: true From 3b0067970a26bb3ee978460fec5250137d7c5380 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 16:26:10 -0400 Subject: [PATCH 08/14] Reran prettier on some files that were overlooked --- .../ExampleRegistrationHandlerTest.cls | 14 +----------- scripts/dev/link-community-auth-provider.js | 22 ++++--------------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls index ce62673ab..f66550e8a 100644 --- a/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls +++ b/config/scratch-orgs/experience-cloud/classes/ExampleRegistrationHandlerTest.cls @@ -47,19 +47,7 @@ private class ExampleRegistrationHandlerTest { private static Auth.UserData makeUserData(String identifier, String firstName, String lastName, String email) { Map attributeMap = new Map(); - return new Auth.UserData( - identifier, - firstName, - lastName, - firstName + ' ' + lastName, - email, - null, - email, - 'en_US', - 'Google', - null, - attributeMap - ); + return new Auth.UserData(identifier, firstName, lastName, firstName + ' ' + lastName, email, null, email, 'en_US', 'Google', null, attributeMap); } private static Schema.User createInternalUser(String email, String federationIdentifier) { diff --git a/scripts/dev/link-community-auth-provider.js b/scripts/dev/link-community-auth-provider.js index 6b7db95ea..20245b26a 100644 --- a/scripts/dev/link-community-auth-provider.js +++ b/scripts/dev/link-community-auth-provider.js @@ -12,9 +12,7 @@ const { values } = parseArgs({ }); if (!values.network || !values['auth-provider']) { - console.error( - 'Usage: node ./scripts/dev/link-community-auth-provider.js --network --auth-provider [--target-org ]' - ); + console.error('Usage: node ./scripts/dev/link-community-auth-provider.js --network --auth-provider [--target-org ]'); process.exit(1); } @@ -65,17 +63,13 @@ function querySingle(soql) { return response.result.records?.[0]; } -const network = querySingle( - `SELECT Id, Name, UrlPathPrefix FROM Network WHERE Name = '${escapeSoql(networkName)}' LIMIT 1` -); +const network = querySingle(`SELECT Id, Name, UrlPathPrefix FROM Network WHERE Name = '${escapeSoql(networkName)}' LIMIT 1`); if (!network || !network.UrlPathPrefix) { console.error(`Could not find Network '${networkName}' or its UrlPathPrefix.`); process.exit(1); } -const authProvider = querySingle( - `SELECT Id, DeveloperName FROM AuthProvider WHERE DeveloperName = '${escapeSoql(authProviderDeveloperName)}' LIMIT 1` -); +const authProvider = querySingle(`SELECT Id, DeveloperName FROM AuthProvider WHERE DeveloperName = '${escapeSoql(authProviderDeveloperName)}' LIMIT 1`); if (!authProvider) { console.error(`Could not find AuthProvider with DeveloperName '${authProviderDeveloperName}'.`); process.exit(1); @@ -100,15 +94,7 @@ const existingLink = querySingle( if (existingLink) { console.info(`Auth provider is already linked (AuthConfigProviders.Id=${existingLink.Id}).`); } else { - runSf([ - 'data', - 'create', - 'record', - '--sobject', - 'AuthConfigProviders', - '--values', - `AuthConfigId=${authConfig.Id} AuthProviderId=${authProvider.Id}` - ]); + runSf(['data', 'create', 'record', '--sobject', 'AuthConfigProviders', '--values', `AuthConfigId=${authConfig.Id} AuthProviderId=${authProvider.Id}`]); console.info('Linked auth provider to community login configuration.'); } From 69ba5ab624371fbcf0ac5bb0921be22b03294ba4 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 16:52:32 -0400 Subject: [PATCH 09/14] Updated GitHub Actions to Node 24 major versions (GitHub is deprecating the previously used versions, built for Node 20) --- .github/actions/setup-npm/action.yml | 2 +- .github/workflows/build.yml | 14 +++++++------- .github/workflows/delete-stale-scratch-orgs.yml | 2 +- .github/workflows/publish-docker-container.yml | 8 ++++---- .github/workflows/test-scratch-org.yml | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/actions/setup-npm/action.yml b/.github/actions/setup-npm/action.yml index 0a805b713..cfc367aab 100644 --- a/.github/actions/setup-npm/action.yml +++ b/.github/actions/setup-npm/action.yml @@ -8,7 +8,7 @@ runs: steps: - name: 'Restore node_modules cache' id: cache-npm - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: node_modules key: npm-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb646b657..3c37bf656 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,10 +31,10 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Check for changes in core directory' - uses: dorny/paths-filter@v3 + uses: dorny/paths-filter@v4 id: changes with: filters: | @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Setup npm' uses: ./.github/actions/setup-npm @@ -85,7 +85,7 @@ jobs: run: npm run test:lwc - name: 'Upload LWC code coverage to Codecov.io' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: true flags: LWC @@ -160,7 +160,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.ref }} @@ -187,7 +187,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.ref }} @@ -306,7 +306,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Setup npm' uses: ./.github/actions/setup-npm diff --git a/.github/workflows/delete-stale-scratch-orgs.yml b/.github/workflows/delete-stale-scratch-orgs.yml index da7d4c370..e51ed3fdb 100644 --- a/.github/workflows/delete-stale-scratch-orgs.yml +++ b/.github/workflows/delete-stale-scratch-orgs.yml @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Setup npm' uses: ./.github/actions/setup-npm diff --git a/.github/workflows/publish-docker-container.yml b/.github/workflows/publish-docker-container.yml index c8d8ddafd..e0b769cbb 100644 --- a/.github/workflows/publish-docker-container.yml +++ b/.github/workflows/publish-docker-container.yml @@ -11,17 +11,17 @@ jobs: permissions: packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: docker/setup-buildx-action@v3 + - uses: docker/setup-buildx-action@v4 - - uses: docker/login-action@v3 + - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GH_TOKEN_PUBLISH_DOCKER_IMAGE }} - - uses: docker/build-push-action@v5 + - uses: docker/build-push-action@v7 with: context: . file: .devcontainer/Dockerfile diff --git a/.github/workflows/test-scratch-org.yml b/.github/workflows/test-scratch-org.yml index 01e5a4647..da9f9de55 100644 --- a/.github/workflows/test-scratch-org.yml +++ b/.github/workflows/test-scratch-org.yml @@ -57,7 +57,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Setup npm' uses: ./.github/actions/setup-npm @@ -124,7 +124,7 @@ jobs: - name: 'Upload Apex test code coverage to Codecov.io' if: ${{ inputs.upload-apex-coverage }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: true flags: Apex From 8c510777752903d96debfc6119769b814aa7f59c Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 18:12:14 -0400 Subject: [PATCH 10/14] Reverted the step in authorize-dev-hub action that deleted the server JWT file so that it's still available for subsequent sf CLI commands I thought deleting it would be safe & a good idea for security - but it broke everything ~_~ --- .github/actions/authorize-dev-hub/action.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/actions/authorize-dev-hub/action.yml b/.github/actions/authorize-dev-hub/action.yml index 0f4745396..791ecb24f 100644 --- a/.github/actions/authorize-dev-hub/action.yml +++ b/.github/actions/authorize-dev-hub/action.yml @@ -20,6 +20,12 @@ inputs: runs: using: 'composite' steps: + # The key file must remain on disk for the rest of the job. The SF CLI records the + # key's path (not its contents) in ~/.sfdx/ and re-reads it whenever it has to refresh + # the JWT - including the implicit re-auth that `sf org create scratch` performs when + # authenticating into the new scratch org. Deleting the file mid-job causes ENOENT on + # the next refresh. Cleanup is unnecessary anyway: GitHub-hosted runners are single-use + # ephemeral VMs that get wiped after the job ends. - name: 'Authorize Dev Hub' shell: bash env: @@ -31,8 +37,3 @@ runs: npx sf version echo "$DEV_HUB_JWT_SERVER_KEY" > ./jwt-server.key npx sf org login jwt --instance-url "$DEV_HUB_AUTH_URL" --client-id "$DEV_HUB_CONSUMER_KEY" --username "$DEV_HUB_BOT_USERNAME" --jwt-key-file ./jwt-server.key --set-default-dev-hub - - - name: 'Delete JWT key file' - if: ${{ always() }} - shell: bash - run: rm -f ./jwt-server.key From ea97eea27833469325805166efcaa364cfd0c3fe Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 18:57:12 -0400 Subject: [PATCH 11/14] Added the fancy new plugin @jongpie/sf-dotenv-cli-plugin & a small node script to export the current user's username to .env so that deploying the example email service works in the pipeline --- .github/actions/setup-npm/action.yml | 6 + .github/workflows/build.yml | 5 +- .github/workflows/test-scratch-org.yml | 5 + .gitignore | 1 + .../ExampleInboundEmailService.xml-meta.xml | 2 +- package-lock.json | 1709 ++--------------- package.json | 5 +- scripts/dev/set-email-service-run-as-user.js | 46 + sfdx-project.json | 7 + 9 files changed, 283 insertions(+), 1503 deletions(-) create mode 100644 scripts/dev/set-email-service-run-as-user.js diff --git a/.github/actions/setup-npm/action.yml b/.github/actions/setup-npm/action.yml index cfc367aab..bdfb20b64 100644 --- a/.github/actions/setup-npm/action.yml +++ b/.github/actions/setup-npm/action.yml @@ -20,3 +20,9 @@ runs: if: steps.cache-npm.outputs.cache-hit != 'true' shell: bash run: npm ci + + - name: 'Link sf CLI plugins from node_modules' + shell: bash + run: | + npm run sf:plugins:link:bummer + npm run sf:plugins:link:dotenv diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c37bf656..63eb72424 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -205,6 +205,9 @@ jobs: - name: 'Create Scratch Org' run: npx sf org create scratch --no-namespace --no-track-source --alias base_package_subscriber_scratch_org --duration-days 1 --definition-file ./config/scratch-orgs/base-scratch-def.json --wait 20 --set-default --json + - name: 'Set EMAIL_SERVICE_RUN_AS_USER for the scratch org' + run: npm run env:set:email-service-run-as-user + - name: 'Create & Install Package Version' run: npx pwsh ./scripts/build/create-and-install-package-version.ps1 -targetpackagealias '"Nebula Logger - Core"' -targetreadme ./README.md -targetusername base_package_subscriber_scratch_org @@ -278,8 +281,6 @@ jobs: - name: 'Commit new package version' if: ${{ github.event_name == 'pull_request' }} run: | - # npm run sf:plugins:link:bummer - echo y | npx sf plugins install @jongpie/sfdx-bummer-plugin --force npx sf bummer:package:aliases:sort npx prettier --write ./sfdx-project.json git add ./sfdx-project.json diff --git a/.github/workflows/test-scratch-org.yml b/.github/workflows/test-scratch-org.yml index da9f9de55..6ab29fdfd 100644 --- a/.github/workflows/test-scratch-org.yml +++ b/.github/workflows/test-scratch-org.yml @@ -81,6 +81,11 @@ jobs: SCRATCH_ORG_DESCRIPTION: 'Nebula Logger pipeline run ${{ github.run_id }} attempt ${{ github.run_attempt }}' run: npx sf org create scratch --no-namespace --no-track-source --duration-days 1 --definition-file "$DEFINITION_FILE" --description "$SCRATCH_ORG_DESCRIPTION" --alias pipeline-scratch-org --wait 20 --set-default --json + # The replacements block in sfdx-project.json swaps {{EMAIL_SERVICE_RUN_AS_USER}} in + # ExampleInboundEmailService.xml-meta.xml with the value of this env var at deploy time. + - name: 'Set EMAIL_SERVICE_RUN_AS_USER for the scratch org' + run: npm run env:set:email-service-run-as-user + # https://help.salesforce.com/s/articleView?id=000394906&type=1 - name: "Install OmniStudio managed package v258.6 (Winter '26 release)" if: ${{ inputs.install-omnistudio }} diff --git a/.gitignore b/.gitignore index a02dbe5fa..c5ec6ed19 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ node_modules/ yarn.lock # Files to exclude +*.env *.log **/lwc/jsconfig.json .config diff --git a/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml b/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml index 13c6ed79b..90f791b75 100644 --- a/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml +++ b/nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml @@ -9,7 +9,7 @@ ExampleEmailAddress true exampleinboundemailservice - test-a9tdmyxmog79@example.com + {{EMAIL_SERVICE_RUN_AS_USER}} Discard ExampleInboundEmailService diff --git a/package-lock.json b/package-lock.json index cc866b1da..9c9024892 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nebula-logger", - "version": "4.18.3", + "version": "4.18.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nebula-logger", - "version": "4.18.3", + "version": "4.18.4", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -14,6 +14,7 @@ }, "devDependencies": { "@cparra/apexdocs": "1.13.7", + "@jongpie/sf-dotenv-cli-plugin": "^1.0.0", "@jongpie/sfdx-bummer-plugin": "0.0.20", "@salesforce-ux/slds-linter": "1.2.0", "@salesforce/cli": "2.134.1", @@ -2676,6 +2677,118 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jongpie/sf-dotenv-cli-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jongpie/sf-dotenv-cli-plugin/-/sf-dotenv-cli-plugin-1.0.0.tgz", + "integrity": "sha512-V/MklEaCQJYa+sHzKtsyBbZMcwGX/zQleiw2Q4syLPolhiX3xFOzdpGy3bhFzrFoQjLG2vNNXMsrswg9ofG0fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "4.10.2", + "dotenv": "17.3.1", + "fs-extra": "11.3.4" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/@oclif/core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.2.tgz", + "integrity": "sha512-3GvDh5nqpIE8566qUF5cBHKog9DFV9XgBeuR0nUrz0OMuz2FPYHat1AZHOwyQbvH9OKL4gJNQZHcsDOqDM/FRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.4", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jongpie/sf-dotenv-cli-plugin/node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@jongpie/sfdx-bummer-plugin": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@jongpie/sfdx-bummer-plugin/-/sfdx-bummer-plugin-0.0.20.tgz", @@ -5867,16 +5980,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@salesforce/cli/node_modules/@gar/promise-retry": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", - "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -6868,13 +6971,6 @@ "node": ">=18.0.0" } }, - "node_modules/@salesforce/cli/node_modules/@isaacs/string-locale-compare": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", - "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", - "extraneous": true, - "license": "ISC" - }, "node_modules/@salesforce/cli/node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -7213,287 +7309,6 @@ "node": ">= 8" } }, - "node_modules/@salesforce/cli/node_modules/@npmcli/agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", - "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^11.2.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/arborist": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.4.3.tgz", - "integrity": "sha512-YhkR7XFdO7OBr8U1qs7DA7PmhSJXg59rLqd53jmeJ4pYe8WTCAsUZsKqxX7KKPEgAO5K7D/SjbyPUrBes9aP6Q==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^5.0.0", - "@npmcli/installed-package-contents": "^4.0.0", - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/metavuln-calculator": "^9.0.2", - "@npmcli/name-from-folder": "^4.0.0", - "@npmcli/node-gyp": "^5.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/query": "^5.0.0", - "@npmcli/redact": "^4.0.0", - "@npmcli/run-script": "^10.0.0", - "bin-links": "^6.0.0", - "cacache": "^20.0.1", - "common-ancestor-path": "^2.0.0", - "hosted-git-info": "^9.0.0", - "json-stringify-nice": "^1.1.4", - "lru-cache": "^11.2.1", - "minimatch": "^10.0.3", - "nopt": "^9.0.0", - "npm-install-checks": "^8.0.0", - "npm-package-arg": "^13.0.0", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "pacote": "^21.0.2", - "parse-conflict-json": "^5.0.1", - "proc-log": "^6.0.0", - "proggy": "^4.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^3.0.1", - "semver": "^7.3.7", - "ssri": "^13.0.0", - "treeverse": "^3.0.0", - "walk-up-path": "^4.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/config": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/@npmcli/config/-/config-10.8.1.tgz", - "integrity": "sha512-MAYk9IlIGiyC0c9fnjdBSQfIFPZT0g1MfeSiD1UXTq2zJOLX55jS9/sETJHqw/7LN18JjITrhYfgCfapbmZHiQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/map-workspaces": "^5.0.0", - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "ini": "^6.0.0", - "nopt": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/config/node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/fs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", - "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/git": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", - "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/promise-spawn": "^9.0.0", - "ini": "^6.0.0", - "lru-cache": "^11.2.1", - "npm-pick-manifest": "^11.0.1", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "which": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/git/node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/installed-package-contents": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", - "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^5.0.0", - "npm-normalize-package-bin": "^5.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/map-workspaces": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-5.0.3.tgz", - "integrity": "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "glob": "^13.0.0", - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.3.tgz", - "integrity": "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "cacache": "^20.0.0", - "json-parse-even-better-errors": "^5.0.0", - "pacote": "^21.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/name-from-folder": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-4.0.0.tgz", - "integrity": "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/node-gyp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", - "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/package-json": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", - "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "glob": "^13.0.0", - "hosted-git-info": "^9.0.0", - "json-parse-even-better-errors": "^5.0.0", - "proc-log": "^6.0.0", - "semver": "^7.5.3", - "spdx-expression-parse": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/promise-spawn": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", - "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "which": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-5.0.0.tgz", - "integrity": "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/redact": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", - "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@npmcli/run-script": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz", - "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^5.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^9.0.0", - "node-gyp": "^12.1.0", - "proc-log": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/@oclif/core": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.0.tgz", @@ -10964,86 +10779,6 @@ "extraneous": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/@sigstore/bundle": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", - "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@sigstore/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.0.tgz", - "integrity": "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==", - "extraneous": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@sigstore/protobuf-specs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.1.tgz", - "integrity": "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==", - "extraneous": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@sigstore/sign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", - "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@gar/promise-retry": "^1.0.2", - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.2.0", - "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.4", - "proc-log": "^6.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@sigstore/tuf": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", - "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@sigstore/verify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", - "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -11946,30 +11681,6 @@ "extraneous": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@salesforce/cli/node_modules/@tufjs/models": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", - "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^10.1.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -12428,16 +12139,6 @@ "extraneous": true, "license": "BSD-2-Clause" }, - "node_modules/@salesforce/cli/node_modules/abbrev": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", - "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -12515,8 +12216,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -12529,8 +12232,10 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -12690,19 +12395,14 @@ } } }, - "node_modules/@salesforce/cli/node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "extraneous": true, - "license": "ISC" - }, "node_modules/@salesforce/cli/node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/are-docs-informative": { "version": "0.0.2", @@ -13114,62 +12814,6 @@ "extraneous": true, "license": "Apache-2.0" }, - "node_modules/@salesforce/cli/node_modules/bin-links": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", - "integrity": "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^8.0.0", - "npm-normalize-package-bin": "^5.0.0", - "proc-log": "^6.0.0", - "read-cmd-shim": "^6.0.0", - "write-file-atomic": "^7.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/bin-links/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@salesforce/cli/node_modules/bin-links/node_modules/write-file-atomic": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", - "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/binary-extensions": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-3.1.0.tgz", - "integrity": "sha512-Jvvd9hy1w+xUad8+ckQsWA/V1AoyubOvqn0aygjMOVM4BfIaRav1NFS3LsTSDaV4n4FtcCtQXvzep1E6MboqwQ==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@salesforce/cli/node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -13339,41 +12983,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@salesforce/cli/node_modules/cacache": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", - "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^5.0.0", - "fs-minipass": "^3.0.0", - "glob": "^13.0.0", - "lru-cache": "^11.1.0", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/cacache/node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@salesforce/cli/node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -13778,7 +13387,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "extraneous": true, + "dev": true, "funding": [ { "type": "github", @@ -13786,20 +13395,12 @@ } ], "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/@salesforce/cli/node_modules/cidr-regex": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-5.0.5.tgz", - "integrity": "sha512-59tdLZcC+BJXa4C5rOmVSuJTy/UneqfJJtCraqwdx5BDHTkGrBtKCUl3u2uiCFvXu+wk0kVuX8axX7yHCZOI9w==", - "extraneous": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=20" - } - }, "node_modules/@salesforce/cli/node_modules/cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -14129,16 +13730,6 @@ "semver": "bin/semver" } }, - "node_modules/@salesforce/cli/node_modules/cmd-shim": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-8.0.0.tgz", - "integrity": "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/code-excerpt": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", @@ -14504,16 +14095,6 @@ "node": ">=12" } }, - "node_modules/@salesforce/cli/node_modules/common-ancestor-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", - "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", - "extraneous": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">= 18" - } - }, "node_modules/@salesforce/cli/node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -15119,19 +14700,6 @@ "node": ">=0.6.0" } }, - "node_modules/@salesforce/cli/node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "extraneous": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@salesforce/cli/node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -15526,8 +15094,10 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "extraneous": true, + "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } @@ -15828,16 +15398,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/@salesforce/cli/node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/@salesforce/cli/node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -16631,13 +16191,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/@salesforce/cli/node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "extraneous": true, - "license": "Apache-2.0" - }, "node_modules/@salesforce/cli/node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -17135,19 +16688,6 @@ "node": ">=14.14" } }, - "node_modules/@salesforce/cli/node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@salesforce/cli/node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -17552,24 +17092,6 @@ "extraneous": true, "license": "ISC" }, - "node_modules/@salesforce/cli/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "extraneous": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@salesforce/cli/node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -18035,19 +17557,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/hpagent": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", @@ -18254,19 +17763,6 @@ "node": ">= 4" } }, - "node_modules/@salesforce/cli/node_modules/ignore-walk": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", - "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minimatch": "^10.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -18318,8 +17814,10 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.8.19" } @@ -18359,34 +17857,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@salesforce/cli/node_modules/init-package-json": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-8.2.5.tgz", - "integrity": "sha512-IknQ+upLuJU6t3p0uo9wS3GjFD/1GtxIwcIGYOWR8zL2HxQeJwvxYTgZr9brJ8pyZ4kvpkebM8ZKcyqOeLOHSg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "npm-package-arg": "^13.0.0", - "promzard": "^3.0.1", - "read": "^5.0.1", - "semver": "^7.7.2", - "validate-npm-package-name": "^7.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/init-package-json/node_modules/validate-npm-package-name": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", - "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/ink": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ink/-/ink-5.0.1.tgz", @@ -18948,19 +18418,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@salesforce/cli/node_modules/is-cidr": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-6.0.4.tgz", - "integrity": "sha512-tOIBU3QiXy0W4LvHbcKWAWSuQfGwDiEILphFCAZtDqj7C57uv3ClO6K8aNEGV4VTA7bWJlpQ0suKQkUe6Rd6ag==", - "extraneous": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^5.0.4" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/@salesforce/cli/node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -19322,8 +18779,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/is-unicode-supported": { "version": "0.1.0", @@ -19381,16 +18840,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/isexe": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", - "extraneous": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=20" - } - }, "node_modules/@salesforce/cli/node_modules/isomorphic-git": { "version": "1.34.2", "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.34.2.tgz", @@ -19710,16 +19159,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/json-parse-even-better-errors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", - "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -19744,16 +19183,6 @@ "node": ">=7.10.1" } }, - "node_modules/@salesforce/cli/node_modules/json-stringify-nice": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", - "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", - "extraneous": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@salesforce/cli/node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -19798,11 +19227,13 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, "engines": [ "node >= 0.2.0" ], - "extraneous": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/JSONStream": { "version": "1.3.5", @@ -19894,15 +19325,10 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz", "integrity": "sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==", - "extraneous": true, - "license": "MIT" - }, - "node_modules/@salesforce/cli/node_modules/just-diff-apply": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", - "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/just-extend": { "version": "4.2.1", @@ -19968,194 +19394,6 @@ "node": ">= 0.8.0" } }, - "node_modules/@salesforce/cli/node_modules/libnpmaccess": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-10.0.3.tgz", - "integrity": "sha512-JPHTfWJxIK+NVPdNMNGnkz4XGX56iijPbe0qFWbdt68HL+kIvSzh+euBL8npLZvl2fpaxo+1eZSdoG15f5YdIQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmdiff": { - "version": "8.1.6", - "resolved": "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-8.1.6.tgz", - "integrity": "sha512-nr6/MrxRnqMUoB9t0aHImBKArkJCU3YeaTyu817XYQXAQq9iWgX+ZVLgd+5wZVfoyemPdJj2LasXhFNyVk5GAA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.4.3", - "@npmcli/installed-package-contents": "^4.0.0", - "binary-extensions": "^3.0.0", - "diff": "^8.0.2", - "minimatch": "^10.0.3", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "tar": "^7.5.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmdiff/node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "extraneous": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmexec": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-10.2.6.tgz", - "integrity": "sha512-aUHRHUhoi98CW9x+0+RzOVvKvl4rvGgr6o7wnWfdyuvZtU5WXGStfuArN1wBANxEP50bLTocMJrEsBktEuiVqw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/arborist": "^9.4.3", - "@npmcli/package-json": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2", - "proc-log": "^6.0.0", - "read": "^5.0.1", - "semver": "^7.3.7", - "signal-exit": "^4.1.0", - "walk-up-path": "^4.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmexec/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmfund": { - "version": "7.0.20", - "resolved": "https://registry.npmjs.org/libnpmfund/-/libnpmfund-7.0.20.tgz", - "integrity": "sha512-H1FvUdssvUlAfQJsNotf+DUetF2mS7d2sW8+MByLCMmgsZ+OkKbXgQit0PCjAwg8BD/Z/f8UO0FJT7bOYe73fQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.4.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmorg": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-8.0.1.tgz", - "integrity": "sha512-/QeyXXg4hqMw0ESM7pERjIT2wbR29qtFOWIOug/xO4fRjS3jJJhoAPQNsnHtdwnCqgBdFpGQ45aIdFFZx2YhTA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmpack": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/libnpmpack/-/libnpmpack-9.1.6.tgz", - "integrity": "sha512-Uov/MsMO+1MdJdT4PKdz6MiLNuZb73REKxbxKXKcNUaDkeBGNXxGB1GUxpdsvZlx1sos4MQDTYw34q4yw7hzHw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^9.4.3", - "@npmcli/run-script": "^10.0.0", - "npm-package-arg": "^13.0.0", - "pacote": "^21.0.2" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmpublish": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-11.1.3.tgz", - "integrity": "sha512-NVPTth/71cfbdYHqypcO9Lt5WFGTzFEcx81lWd7GDJIgZ95ERdYHGUfCtFejHCyqodKsQkNEx2JCkMpreDty/A==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/package-json": "^7.0.0", - "ci-info": "^4.0.0", - "npm-package-arg": "^13.0.0", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.7", - "sigstore": "^4.0.0", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmsearch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-9.0.1.tgz", - "integrity": "sha512-oKw58X415ERY/BOGV3jQPVMcep8YeMRWMzuuqB0BAIM5VxicOU1tQt19ExCu4SV77SiTOEoziHxGEgJGw3FBYQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmteam": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-8.0.2.tgz", - "integrity": "sha512-ypLrDUQoi8EhG+gzx5ENMcYq23YjPV17Mfvx4nOnQiHOi8vp47+4GvZBrMsEM4yeHPwxguF/HZoXH4rJfHdH/w==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^19.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/libnpmversion": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/libnpmversion/-/libnpmversion-8.0.3.tgz", - "integrity": "sha512-Avj1GG3DT6MGzWOOk3yA7rORcMDUPizkIGbI8glHCO7WoYn3NYNmskLDwxg2NMY1Tyf2vrHAqTuSG58uqd1lJg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^7.0.0", - "@npmcli/run-script": "^10.0.0", - "json-parse-even-better-errors": "^5.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -20666,30 +19904,6 @@ "extraneous": true, "license": "ISC" }, - "node_modules/@salesforce/cli/node_modules/make-fetch-happen": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz", - "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/agent": "^4.0.0", - "@npmcli/redact": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^5.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^6.0.0", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -21169,102 +20383,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/@salesforce/cli/node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-fetch": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", - "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^2.0.0", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - }, - "optionalDependencies": { - "iconv-lite": "^0.7.2" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@salesforce/cli/node_modules/minipass-sized": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", - "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@salesforce/cli/node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", @@ -21537,16 +20655,6 @@ "node": ">=18" } }, - "node_modules/@salesforce/cli/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@salesforce/cli/node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -21668,41 +20776,6 @@ } } }, - "node_modules/@salesforce/cli/node_modules/node-gyp": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.3.0.tgz", - "integrity": "sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "graceful-fs": "^4.2.6", - "nopt": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "tar": "^7.5.4", - "tinyglobby": "^0.2.12", - "undici": "^6.25.0", - "which": "^6.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/node-gyp/node_modules/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, "node_modules/@salesforce/cli/node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -21723,22 +20796,6 @@ "extraneous": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/nopt": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", - "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "abbrev": "^4.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -21947,142 +21004,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@salesforce/cli/node_modules/npm-audit-report": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-7.0.0.tgz", - "integrity": "sha512-bluLL4xwGr/3PERYz50h2Upco0TJMDcLcymuFnfDWeGO99NqH724MNzhWi5sXXuXf2jbytFF0LyR8W+w1jTI6A==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-bundled": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", - "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-install-checks": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", - "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", - "extraneous": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-normalize-package-bin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", - "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-package-arg": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", - "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^9.0.0", - "proc-log": "^6.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^7.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-package-arg/node_modules/validate-npm-package-name": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", - "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-packlist": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz", - "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^8.0.0", - "proc-log": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-pick-manifest": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", - "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^8.0.0", - "npm-normalize-package-bin": "^5.0.0", - "npm-package-arg": "^13.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-profile": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-12.0.1.tgz", - "integrity": "sha512-Xs1mejJ1/9IKucCxdFMkiBJUre0xaxfCpbsO7DB7CadITuT4k68eI05HBlw4kj+Em1rsFMgeFNljFPYvPETbVQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^19.0.0", - "proc-log": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/npm-registry-fetch": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", - "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@npmcli/redact": "^4.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^15.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^5.0.0", - "minizlib": "^3.0.1", - "npm-package-arg": "^13.0.0", - "proc-log": "^6.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -22096,16 +21017,6 @@ "node": ">=8" } }, - "node_modules/@salesforce/cli/node_modules/npm-user-validate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-4.0.0.tgz", - "integrity": "sha512-TP+Ziq/qPi/JRdhaEhnaiMkqfMGjhDLoh/oRfW+t5aCuIfJxIUxvwk6Sg/6ZJ069N/Be6gs00r+aZeJTfS9uHQ==", - "extraneous": true, - "license": "BSD-2-Clause", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/npm/node_modules/@gar/promise-retry": { "version": "1.0.3", "dev": true, @@ -24493,8 +23404,10 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "aggregate-error": "^3.0.0" }, @@ -24569,38 +23482,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/@salesforce/cli/node_modules/pacote": { - "version": "21.5.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.0.tgz", - "integrity": "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/git": "^7.0.0", - "@npmcli/installed-package-contents": "^4.0.0", - "@npmcli/package-json": "^7.0.0", - "@npmcli/promise-spawn": "^9.0.0", - "@npmcli/run-script": "^10.0.0", - "cacache": "^20.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^7.0.2", - "npm-package-arg": "^13.0.0", - "npm-packlist": "^10.0.1", - "npm-pick-manifest": "^11.0.1", - "npm-registry-fetch": "^19.0.0", - "proc-log": "^6.0.0", - "sigstore": "^4.0.0", - "ssri": "^13.0.0", - "tar": "^7.4.3" - }, - "bin": { - "pacote": "bin/index.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -24632,28 +23513,6 @@ "node": ">=6" } }, - "node_modules/@salesforce/cli/node_modules/parse-conflict-json": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-5.0.1.tgz", - "integrity": "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^5.0.0", - "just-diff": "^6.0.0", - "just-diff-apply": "^5.2.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/parse-conflict-json/node_modules/just-diff": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", - "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", - "extraneous": true, - "license": "MIT" - }, "node_modules/@salesforce/cli/node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -25005,20 +23864,6 @@ "node": ">= 0.4" } }, - "node_modules/@salesforce/cli/node_modules/postcss-selector-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@salesforce/cli/node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -25133,16 +23978,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@salesforce/cli/node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -25190,49 +24025,6 @@ ], "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/proggy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/proggy/-/proggy-4.0.0.tgz", - "integrity": "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", - "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", - "extraneous": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@salesforce/cli/node_modules/promise-call-limit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.1.tgz", - "integrity": "sha512-utl+0x8gIDasV5X+PI5qWEPqH6fJS0pFtQ/4gZ95xfEFb/89dmh+/b895TbFDBLiafBvxD/PGTKfvxl4kH/pQg==", - "extraneous": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@salesforce/cli/node_modules/promzard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/promzard/-/promzard-3.0.1.tgz", - "integrity": "sha512-M5mHhWh+Adz0BIxgSrqcc6GTCSconR7zWQV9vnOSptNtr6cSFlApLc28GbQhuN6oOWBQeV2C0bNE47JCY/zu3Q==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "read": "^5.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/propagate": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", @@ -25383,15 +24175,6 @@ "node": ">=6" } }, - "node_modules/@salesforce/cli/node_modules/qrcode-terminal": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", - "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", - "extraneous": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, "node_modules/@salesforce/cli/node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -25489,29 +24272,6 @@ "react": "^18.3.1" } }, - "node_modules/@salesforce/cli/node_modules/read": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/read/-/read-5.0.1.tgz", - "integrity": "sha512-+nsqpqYkkpet2UVPG8ZiuE8d113DK4vHYEoEhcrXBAlPiq6di7QRTuNiKQAbaRYegobuX2BpZ6QjanKOXnJdTA==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "mute-stream": "^3.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@salesforce/cli/node_modules/read-cmd-shim": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", - "integrity": "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -25610,16 +24370,6 @@ "node": ">=4" } }, - "node_modules/@salesforce/cli/node_modules/read/node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -26376,24 +25126,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@salesforce/cli/node_modules/sigstore": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", - "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0", - "@sigstore/sign": "^4.1.0", - "@sigstore/tuf": "^4.0.1", - "@sigstore/verify": "^3.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -26766,15 +25498,19 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "extraneous": true, - "license": "CC-BY-3.0" + "dev": true, + "license": "CC-BY-3.0", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/spdx-expression-parse": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -26784,8 +25520,10 @@ "version": "3.0.17", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "extraneous": true, - "license": "CC0-1.0" + "dev": true, + "license": "CC0-1.0", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/split": { "version": "1.0.1", @@ -26830,19 +25568,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@salesforce/cli/node_modules/ssri": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", - "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/stack-chain": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", @@ -27294,8 +26019,10 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "extraneous": true, - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/thingies": { "version": "2.5.0", @@ -27348,13 +26075,6 @@ "extraneous": true, "license": "MIT" }, - "node_modules/@salesforce/cli/node_modules/tiny-relative-date": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-2.0.2.tgz", - "integrity": "sha512-rGxAbeL9z3J4pI2GtBEoFaavHdO4RKAU54hEuOef5kfx5aPqiQtbhYktMOTL5OA33db8BjsDcLXuNp+/v19PHw==", - "extraneous": true, - "license": "MIT" - }, "node_modules/@salesforce/cli/node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -27485,16 +26205,6 @@ "tslib": "2" } }, - "node_modules/@salesforce/cli/node_modules/treeverse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", - "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@salesforce/cli/node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -27695,21 +26405,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/@salesforce/cli/node_modules/tuf-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", - "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@tufjs/models": "4.1.0", - "debug": "^4.4.3", - "make-fetch-happen": "^15.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -27844,8 +26539,10 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "extraneous": true, + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "is-typedarray": "^1.0.0" } @@ -28259,16 +26956,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@salesforce/cli/node_modules/walk-up-path": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", - "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", - "extraneous": true, - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@salesforce/cli/node_modules/web-vitals": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", @@ -28319,22 +27006,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/@salesforce/cli/node_modules/which": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", - "extraneous": true, - "license": "ISC", - "dependencies": { - "isexe": "^4.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, "node_modules/@salesforce/cli/node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -28532,8 +27203,10 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "extraneous": true, + "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -28657,8 +27330,10 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "extraneous": true, - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/@salesforce/cli/node_modules/yaml": { "version": "2.8.3", @@ -30158,6 +28833,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -32031,6 +30716,19 @@ "tslib": "^2.0.3" } }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dtrace-provider": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", @@ -37005,6 +35703,19 @@ "immediate": "~3.0.5" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/line-column": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz", diff --git a/package.json b/package.json index a575725e0..dfca9f625 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "docs:verify": "pwsh ./scripts/build/verify-docs-up-to-date.ps1", "husky:pre-commit": "lint-staged", "lwc:preview:recipes": "sf lightning dev app --device-type desktop --name \"Logger Recipes\"", + "env:set:email-service-run-as-user": "node ./scripts/dev/set-email-service-run-as-user.js", "scratch:assign:current-user-role": "node ./scripts/dev/assign-role-to-current-user.js", "scratch:link:community:authprovider": "node ./scripts/dev/link-community-auth-provider.js --network \"Logger Test Aura Site\" --auth-provider ExampleGoogleAuth", "package:manifest:generate:core": "sf project generate manifest --source-dir ./nebula-logger/core/ --output-dir ./nebula-logger/ --name core.package", @@ -67,6 +68,7 @@ "scan:lwc:local": "npx sf code-analyzer run --rule-selector \"eslint:(1,2,3)\" --workspace ./nebula-logger/core/**/lwc/** --output-file ./code-analyzer-lwc-eslint-violations.json", "scan:lwc:slds:fix": "npx @salesforce-ux/slds-linter@latest lint ./nebula-logger/core/**/lwc/** --fix", "sf:plugins:link:bummer": "npx sf plugins link ./node_modules/@jongpie/sfdx-bummer-plugin", + "sf:plugins:link:dotenv": "npx sf plugins link ./node_modules/@jongpie/sf-dotenv-cli-plugin", "test": "npm run test:lwc && npm run test:apex", "test:apex": "npm run test:apex:nocoverage -- --code-coverage --detailed-coverage", "test:apex:nocoverage": "npx sf apex run test --concise --test-level RunLocalTests --wait 30 --result-format human --output-dir ./test-coverage/apex", @@ -83,6 +85,7 @@ }, "devDependencies": { "@cparra/apexdocs": "1.13.7", + "@jongpie/sf-dotenv-cli-plugin": "^1.0.0", "@jongpie/sfdx-bummer-plugin": "0.0.20", "@salesforce-ux/slds-linter": "1.2.0", "@salesforce/cli": "2.134.1", @@ -94,8 +97,8 @@ "jest-when": "3.7.0", "jsdoc-to-markdown": "9.1.3", "lint-staged": "16.2.7", - "prettier-plugin-apex": "2.2.6", "prettier": "3.8.1", + "prettier-plugin-apex": "2.2.6", "pwsh": "0.3.0" } } diff --git a/scripts/dev/set-email-service-run-as-user.js b/scripts/dev/set-email-service-run-as-user.js new file mode 100644 index 000000000..86ccb2dad --- /dev/null +++ b/scripts/dev/set-email-service-run-as-user.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +const { spawnSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const ENV_VAR_NAME = 'EMAIL_SERVICE_RUN_AS_USER'; +const envFilePath = path.resolve(process.cwd(), '.env'); + +const sfResult = spawnSync('sf', ['org', 'display', '--json'], { encoding: 'utf8', shell: true }); +if (sfResult.status !== 0) { + console.error(sfResult.stderr || sfResult.stdout); + process.exit(sfResult.status ?? 1); +} +const orgInfo = JSON.parse(sfResult.stdout); +const username = orgInfo.result?.username; +if (!username) { + console.error('Could not resolve the current org username from `sf org display --json`.'); + process.exit(1); +} + +const newLine = `${ENV_VAR_NAME}=${username}`; +const existingContents = fs.existsSync(envFilePath) ? fs.readFileSync(envFilePath, 'utf8') : ''; +const lines = existingContents.length > 0 ? existingContents.split(/\r?\n/) : []; + +const matcher = new RegExp(`^${ENV_VAR_NAME}=`); +const matchingIndex = lines.findIndex(line => matcher.test(line)); + +if (matchingIndex >= 0) { + lines[matchingIndex] = newLine; +} else { + if (lines.length > 0 && lines[lines.length - 1] === '') { + lines.splice(lines.length - 1, 0, newLine); + } else { + lines.push(newLine); + } +} + +let updatedContents = lines.join('\n'); +if (!updatedContents.endsWith('\n')) { + updatedContents += '\n'; +} +fs.writeFileSync(envFilePath, updatedContents); + +console.info(`${matchingIndex >= 0 ? 'Updated' : 'Added'} ${ENV_VAR_NAME} in ${envFilePath}`); +console.info(newLine); diff --git a/sfdx-project.json b/sfdx-project.json index 68d7320c4..678224bae 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -84,6 +84,13 @@ "default": false } ], + "replacements": [ + { + "filename": "nebula-logger/extra-tests/integration-tests/emailservices/ExampleInboundEmailService.xml-meta.xml", + "stringToReplace": "{{EMAIL_SERVICE_RUN_AS_USER}}", + "replaceWithEnv": "EMAIL_SERVICE_RUN_AS_USER" + } + ], "packageAliases": { "Nebula Logger - Core": "0Ho5Y000000TNKASA4", "Nebula Logger - Core@4.4.1-unlocked-package-release": "04t5Y0000027FGFQA2", From 60e88945aab5e2a554db2e7c1984dba9d9dab513 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 20:42:11 -0400 Subject: [PATCH 12/14] Reverted (unrelated) changes to testing state & country picklists - it's causing pipeline issues and is out o' scope --- .github/workflows/build.yml | 3 -- config/scratch-orgs/advanced-scratch-def.json | 29 ++----------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63eb72424..543c9f63e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,9 +138,6 @@ jobs: environment: 'Platform Cache Scratch Org' definition-file: ./config/scratch-orgs/platform-cache-scratch-def.json - - label: 'State & Country Picklists' - environment: 'State & Country Picklists Scratch Org' - definition-file: ./config/scratch-orgs/state-and-country-picklists-scratch-def.json uses: ./.github/workflows/test-scratch-org.yml with: environment: ${{ matrix.environment }} diff --git a/config/scratch-orgs/advanced-scratch-def.json b/config/scratch-orgs/advanced-scratch-def.json index 620e28b20..94b57a01d 100644 --- a/config/scratch-orgs/advanced-scratch-def.json +++ b/config/scratch-orgs/advanced-scratch-def.json @@ -2,17 +2,9 @@ "orgName": "Nebula Logger - Advanced Scratch Org", "edition": "Enterprise", "hasSampleData": true, - "features": [ - "Communities", - "ContactsToMultipleAccounts", - "EinsteinGPTPlatform", - "EventLogFile", - "OmniStudioDesigner", - "OmniStudioRuntime", - "PersonAccounts", - "PlatformCache", - "StateAndCountryPicklist" - ], + "country": "US", + "language": "en_US", + "features": ["Communities", "EinsteinGPTPlatform", "EventLogFile", "OmniStudioDesigner", "OmniStudioRuntime", "PlatformCache"], "settings": { "apexSettings": { "enableAuraApexCtrlAuthUserAccessCheckPref": true, @@ -26,9 +18,6 @@ "communitiesSettings": { "enableNetworksEnabled": true }, - "enhancedNotesSettings": { - "enableEnhancedNotes": true - }, "emailAdministrationSettings": { "enableEnhancedEmailEnabled": true }, @@ -48,23 +37,11 @@ "enableOmniStudioMetadata": true, "enableStandardOmniStudioRuntime": true }, - "nameSettings": { - "enableMiddleName": true, - "enableNameSuffix": true - }, "pathAssistantSettings": { "pathAssistantEnabled": true }, "platformEventSettings": { "enableEnhancedUsageMetrics": true - }, - "securitySettings": { - "enableAuditFieldsInactiveOwner": "true", - "sessionSettings": { - "lockSessionsToDomain": "false", - "lockSessionsToIp": "false", - "sessionTimeout": "TwelveHours" - } } } } From 39d4b0f7cc0d998c942d0098cef38348c8acf6c1 Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 4 Jun 2026 20:46:09 -0400 Subject: [PATCH 13/14] Made the pipeline a little bit faster by making 'code-quality-tests' job run at the same time as (instead of as a prerequisite for) 'lwc-jest-tests' and 'scratch-org-tests' --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 543c9f63e..fce7058fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,6 @@ jobs: lwc-jest-tests: name: 'Run LWC Jest Tests' - needs: [code-quality-tests] runs-on: ubuntu-22.04 steps: - name: 'Checkout source code' @@ -102,7 +101,6 @@ jobs: # lower coverage, so the base scratch org acts as the most conservative baseline. scratch-org-tests: name: 'Run ${{ matrix.label }} Scratch Org Tests' - needs: [code-quality-tests] strategy: fail-fast: false matrix: From 3c1b9731446667f1e6cd801257a134c022da6a8a Mon Sep 17 00:00:00 2001 From: GitHub Action Bot Date: Fri, 5 Jun 2026 01:31:46 +0000 Subject: [PATCH 14/14] Created new core unlocked package version --- README.md | 6 +++--- sfdx-project.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ffd1f3570..088daa695 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ The most robust observability solution for Salesforce experts. Built 100% native ## Unlocked Package - v4.18.4 -[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU) -[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU) +[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg7000000Bb13AAC) +[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg7000000Bb13AAC) [![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki) -`sf package install --wait 20 --security-type AdminsOnly --package 04tg70000009GaDAAU` +`sf package install --wait 20 --security-type AdminsOnly --package 04tg7000000Bb13AAC` --- diff --git a/sfdx-project.json b/sfdx-project.json index 678224bae..bbe8075e6 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -238,6 +238,7 @@ "Nebula Logger - Core@4.18.1-new-declarative-configuration-for-ignoring-apex-classes-in-stack-traces": "04tg70000008GVpAAM", "Nebula Logger - Core@4.18.2-new-field-logentry__c.wasloggedbycurrentuser__c": "04tg70000008YZBAA2", "Nebula Logger - Core@4.18.3-prismjs-loading-improvements-&-error-handling": "04tg70000009GaDAAU", + "Nebula Logger - Core@4.18.4-bugfix:-removed-most-of-the-usage-of-userinfo.getsessionid()": "04tg7000000Bb13AAC", "Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.0": "04t5Y0000015lhiQAA", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.1": "04t5Y0000015lhsQAA",