From 4c7ddaa52649ad9d7e9e624b87726e390183e36e Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Wed, 18 Mar 2026 15:22:11 -0500 Subject: [PATCH 1/8] Add disclaimer about onboard status --- .../opportunity-status-processor/handler.js | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index 659958da..9597b12b 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -241,11 +241,51 @@ async function isScrapingAvailable(baseUrl, context, onboardStartTime) { } /** - * Checks scrape results for bot protection blocking - * @param {Array} scrapeResults - Array of scrape URL results - * @param {object} context - The context object with log - * @returns {object|null} Bot protection details if detected, null otherwise + * Checks which audit types have completed since onboardStartTime by querying the database. + * An audit is considered completed if a record exists with auditedAt >= onboardStartTime. + * Falls back conservatively (all pending) if the DB query fails. + * + * @param {string} siteId - The site ID + * @param {Array} auditTypes - Audit types expected for this onboard session + * @param {number} onboardStartTime - Onboarding start timestamp in ms + * @param {object} dataAccess - Data access object + * @param {object} log - Logger + * @returns {Promise<{pendingAuditTypes: Array, completedAuditTypes: Array}>} */ +async function checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, dataAccess, log) { + const pendingAuditTypes = []; + const completedAuditTypes = []; + try { + const { Audit } = dataAccess; + const latestAudits = await Audit.allLatestForSite(siteId); + const auditsByType = {}; + if (latestAudits) { + for (const audit of latestAudits) { + auditsByType[audit.getAuditType()] = audit; + } + } + for (const auditType of auditTypes) { + const audit = auditsByType[auditType]; + if (!audit) { + pendingAuditTypes.push(auditType); + } else { + const auditedAt = new Date(audit.getAuditedAt()).getTime(); + if (onboardStartTime && auditedAt < onboardStartTime) { + // Record exists but predates this onboard session — treat as pending + pendingAuditTypes.push(auditType); + } else { + completedAuditTypes.push(auditType); + } + } + } + } catch (error) { + log.warn(`Could not check audit completion from DB for site ${siteId}: ${error.message}`); + // Conservative fallback: mark all as pending so disclaimer is always shown on error + pendingAuditTypes.push(...auditTypes.filter((t) => !completedAuditTypes.includes(t))); + } + return { pendingAuditTypes, completedAuditTypes }; +} + /** * Analyzes missing opportunities and determines the root cause * @param {Array} missingOpportunities - Array of missing opportunity types @@ -680,6 +720,37 @@ export async function runOpportunityStatusProcessor(message, context) { } else { await say(env, log, slackContext, 'No audit errors found'); } + + // Audit completion disclaimer — check DB for pending audits and warn if any are still running + if (auditTypes.length > 0) { + const { pendingAuditTypes } = await checkAuditCompletionFromDB( + siteId, + auditTypes, + onboardStartTime, + dataAccess, + log, + ); + const isRecheck = taskContext?.isRecheck === true; + if (pendingAuditTypes.length > 0) { + const pendingList = pendingAuditTypes.map(getOpportunityTitle).join(', '); + await say( + env, + log, + slackContext, + `:warning: *Heads-up:* The following audit${pendingAuditTypes.length > 1 ? 's' : ''} ` + + `may still be in progress: *${pendingList}*.\n` + + 'The statuses above reflect data available at this moment and may be incomplete. ' + + `Run \`onboard status ${siteUrl}\` to re-check once all audits have completed.`, + ); + } else if (isRecheck) { + await say( + env, + log, + slackContext, + ':white_check_mark: All audits have completed. The statuses above are up to date.', + ); + } + } } log.info(`Processed ${opportunities.length} opportunities for site ${siteId}`); From e53ccd5646189d6a655f3cfccf99d3df7e3843e3 Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Wed, 18 Mar 2026 15:49:55 -0500 Subject: [PATCH 2/8] tests --- .../opportunity-status-processor.test.js | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js index 8434c86a..1619086a 100644 --- a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js +++ b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js @@ -4122,4 +4122,208 @@ describe('Opportunity Status Processor', () => { ); }); }); + + describe('Audit Completion Disclaimer', () => { + let sayStub; + let disclaimerHandler; + + beforeEach(async () => { + sayStub = sinon.stub().resolves(); + const esmockLocal = (await import('esmock')).default; + disclaimerHandler = await esmockLocal('../../../src/tasks/opportunity-status-processor/handler.js', { + '../../../src/utils/slack-utils.js': { say: sayStub }, + '@adobe/spacecat-shared-utils': { + resolveCanonicalUrl: sinon.stub().callsFake(async (url) => url), + }, + '@adobe/spacecat-shared-rum-api-client': { + default: { + createFrom: sinon.stub().returns({ retrieveDomainkey: sinon.stub().rejects() }), + }, + }, + '@adobe/spacecat-shared-google-client': { + default: { createFrom: sinon.stub().rejects() }, + }, + '@adobe/spacecat-shared-scrape-client': { + ScrapeClient: { + createFrom: sinon.stub().returns({ getScrapeJobsByBaseURL: sinon.stub().resolves([]) }), + }, + }, + '../../../src/utils/cloudwatch-utils.js': { + getAuditStatus: sinon.stub().resolves({ executed: true, failureReason: null }), + }, + '../../../src/utils/bot-detection.js': { + checkAndAlertBotProtection: sinon.stub().resolves(null), + }, + }); + }); + + function makeDisclaimerMessage(onboardStartTime, auditTypes, isRecheck = false) { + return { + siteId: 'test-site-id', + siteUrl: 'https://example.com', + organizationId: 'test-org-id', + taskContext: { + auditTypes, + slackContext: { channelId: 'test-channel', threadTs: 'test-thread' }, + onboardStartTime, + isRecheck, + }, + }; + } + + function makeDisclaimerContext(auditRecords) { + return { + ...context, + dataAccess: { + Site: { + findById: sinon.stub().resolves({ + getOpportunities: sinon.stub().resolves([]), + getBaseURL: sinon.stub().returns('https://example.com'), + }), + }, + SiteTopPage: { + allBySiteIdAndSourceAndGeo: sinon.stub().resolves([]), + }, + Audit: { + allLatestForSite: sinon.stub().resolves(auditRecords), + }, + }, + }; + } + + it('sends pending audit warning when audit record predates onboardStartTime', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv']); + const staleAudit = { + getAuditType: () => 'cwv', + getAuditedAt: () => new Date(onboardStartTime - 1000).toISOString(), + }; + const testContext = makeDisclaimerContext([staleAudit]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match(/may still be in progress.*Core Web Vitals/), + ); + }); + + it('sends pending warning when no audit record exists for an expected type', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv']); + // No audit records at all → cwv is pending + const testContext = makeDisclaimerContext([]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match(/may still be in progress/), + ); + }); + + it('sends "all complete" confirmation when isRecheck=true and all audits have run', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv'], true); + const freshAudit = { + getAuditType: () => 'cwv', + getAuditedAt: () => new Date(onboardStartTime + 1000).toISOString(), + }; + const testContext = makeDisclaimerContext([freshAudit]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + ':white_check_mark: All audits have completed. The statuses above are up to date.', + ); + }); + + it('sends no disclaimer when all audits complete and isRecheck=false', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv'], false); + const freshAudit = { + getAuditType: () => 'cwv', + getAuditedAt: () => new Date(onboardStartTime + 1000).toISOString(), + }; + const testContext = makeDisclaimerContext([freshAudit]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + const disclaimerCalls = sayStub.args.map((a) => a[3]).filter(Boolean); + expect(disclaimerCalls.some((m) => m.includes('may still be in progress'))).to.be.false; + expect(disclaimerCalls.some((m) => m.includes('All audits have completed'))).to.be.false; + }); + + it('skips disclaimer check when auditTypes is empty', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, []); + const testContext = makeDisclaimerContext([]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + // Audit.allLatestForSite should not be called for disclaimer (auditTypes empty) + expect(testContext.dataAccess.Audit.allLatestForSite).to.not.have.been.called; + }); + + it('falls back conservatively when Audit.allLatestForSite throws in disclaimer check', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv']); + const testContext = { + ...context, + dataAccess: { + Site: { + findById: sinon.stub().resolves({ + getOpportunities: sinon.stub().resolves([]), + getBaseURL: sinon.stub().returns('https://example.com'), + }), + }, + SiteTopPage: { + allBySiteIdAndSourceAndGeo: sinon.stub().resolves([]), + }, + Audit: { + allLatestForSite: sinon.stub().rejects(new Error('DB unavailable')), + }, + }, + }; + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + expect(testContext.log.warn).to.have.been.calledWith( + sinon.match(/Could not check audit completion from DB for site test-site-id: DB unavailable/), + ); + // Conservative fallback: pending warning sent + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match(/may still be in progress/), + ); + }); + + it('includes siteUrl in the "run onboard status" hint within pending warning', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['broken-backlinks']); + const staleAudit = { + getAuditType: () => 'broken-backlinks', + getAuditedAt: () => new Date(onboardStartTime - 500).toISOString(), + }; + const testContext = makeDisclaimerContext([staleAudit]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match(/onboard status https:\/\/example\.com/), + ); + }); + }); }); From 329d0de5cdac085687d28a544e01ab758f7f0937 Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Wed, 18 Mar 2026 17:20:20 -0500 Subject: [PATCH 3/8] fix disclaimer message and statues --- .../opportunity-status-processor/handler.js | 69 +++++++++------- .../opportunity-status-processor.test.js | 80 ++++++++++++++++++- 2 files changed, 118 insertions(+), 31 deletions(-) diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index 9597b12b..efbe5530 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -18,7 +18,7 @@ import { resolveCanonicalUrl } from '@adobe/spacecat-shared-utils'; import { getAuditStatus } from '../../utils/cloudwatch-utils.js'; import { checkAndAlertBotProtection } from '../../utils/bot-detection.js'; import { say } from '../../utils/slack-utils.js'; -import { getOpportunitiesForAudit } from './audit-opportunity-map.js'; +import { getOpportunitiesForAudit, getAuditsForOpportunity } from './audit-opportunity-map.js'; import { OPPORTUNITY_DEPENDENCY_MAP } from './opportunity-dependency-map.js'; const TASK_TYPE = 'opportunity-status-processor'; @@ -598,6 +598,15 @@ export async function runOpportunityStatusProcessor(message, context) { statusMessages.push(`GSC ${gscStatus}`); statusMessages.push(`Scraping ${scrapingStatus}`); + // Determine which audits are still pending so opportunity statuses can reflect + // in-progress state (⏳) rather than showing stale data as ✅/❌. + // Only meaningful when we have an onboardStartTime anchor to compare against. + let pendingAuditTypes = []; + if (auditTypes && auditTypes.length > 0 && onboardStartTime) { + // eslint-disable-next-line max-len + ({ pendingAuditTypes } = await checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, dataAccess, log)); + } + // Process opportunities by type to avoid duplicates // Only process opportunities that are expected based on the profile's audit types const processedTypes = new Set(); @@ -626,23 +635,28 @@ export async function runOpportunityStatusProcessor(message, context) { } processedTypes.add(opportunityType); - // eslint-disable-next-line no-await-in-loop - const suggestions = await opportunity.getSuggestions(); - const opportunityTitle = getOpportunityTitle(opportunityType); - const hasSuggestions = suggestions && suggestions.length > 0; - const status = hasSuggestions ? ':white_check_mark:' : ':x:'; - statusMessages.push(`${opportunityTitle} ${status}`); - - // Track failed opportunities (no suggestions) - if (!hasSuggestions) { - // Use informational message for opportunities with zero suggestions - const reason = 'Audit executed successfully, opportunity added, but found no suggestions'; - - failedOpportunities.push({ - title: opportunityTitle, - reason, - }); + + // If the source audit is still running, show ⏳ instead of stale ✅/❌ + const sourceAuditIsPending = getAuditsForOpportunity(opportunityType) + .some((auditType) => pendingAuditTypes.includes(auditType)); + + if (sourceAuditIsPending) { + statusMessages.push(`${opportunityTitle} :hourglass_flowing_sand:`); + } else { + // eslint-disable-next-line no-await-in-loop + const suggestions = await opportunity.getSuggestions(); + const hasSuggestions = suggestions && suggestions.length > 0; + const status = hasSuggestions ? ':white_check_mark:' : ':x:'; + statusMessages.push(`${opportunityTitle} ${status}`); + + // Track failed opportunities (no suggestions) + if (!hasSuggestions) { + failedOpportunities.push({ + title: opportunityTitle, + reason: 'Audit executed successfully, opportunity added, but found no suggestions', + }); + } } } @@ -721,23 +735,22 @@ export async function runOpportunityStatusProcessor(message, context) { await say(env, log, slackContext, 'No audit errors found'); } - // Audit completion disclaimer — check DB for pending audits and warn if any are still running + // Audit completion disclaimer — reuse pendingAuditTypes already computed above. + // Only list audit types that have known opportunity mappings; infrastructure audits + // (auto-suggest, auto-fix, scrape, etc.) are not shown since they don't affect + // the displayed opportunity statuses. if (auditTypes.length > 0) { - const { pendingAuditTypes } = await checkAuditCompletionFromDB( - siteId, - auditTypes, - onboardStartTime, - dataAccess, - log, - ); const isRecheck = taskContext?.isRecheck === true; - if (pendingAuditTypes.length > 0) { - const pendingList = pendingAuditTypes.map(getOpportunityTitle).join(', '); + const relevantPendingTypes = pendingAuditTypes.filter( + (t) => getOpportunitiesForAudit(t).length > 0, + ); + if (relevantPendingTypes.length > 0) { + const pendingList = relevantPendingTypes.map(getOpportunityTitle).join(', '); await say( env, log, slackContext, - `:warning: *Heads-up:* The following audit${pendingAuditTypes.length > 1 ? 's' : ''} ` + `:warning: *Heads-up:* The following audit${relevantPendingTypes.length > 1 ? 's' : ''} ` + `may still be in progress: *${pendingList}*.\n` + 'The statuses above reflect data available at this moment and may be incomplete. ' + `Run \`onboard status ${siteUrl}\` to re-check once all audits have completed.`, diff --git a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js index 1619086a..f25f7903 100644 --- a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js +++ b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js @@ -2491,13 +2491,22 @@ describe('Opportunity Status Processor', () => { message.siteUrl = 'https://example.com'; message.taskContext.auditTypes = ['cwv', 'broken-backlinks']; - message.taskContext.onboardStartTime = Date.now() - 3600000; + const onboardStartTime = Date.now() - 3600000; + message.taskContext.onboardStartTime = onboardStartTime; message.taskContext.slackContext = { channelId: 'test-channel', threadTs: 'test-thread', }; context.env.AWS_REGION = 'us-east-1'; + // Provide fresh audit records so audits are "complete" (not pending) + context.dataAccess.Audit = { + allLatestForSite: sinon.stub().resolves([ + { getAuditType: () => 'cwv', getAuditedAt: () => new Date(onboardStartTime + 1000).toISOString() }, + { getAuditType: () => 'broken-backlinks', getAuditedAt: () => new Date(onboardStartTime + 1000).toISOString() }, + ]), + }; + // Mock TWO opportunities with no suggestions to ensure loop executes multiple times const mockOpportunity1 = { getType: () => 'cwv', @@ -4261,15 +4270,58 @@ describe('Opportunity Status Processor', () => { expect(disclaimerCalls.some((m) => m.includes('All audits have completed'))).to.be.false; }); - it('skips disclaimer check when auditTypes is empty', async () => { + it('skips disclaimer and pending check when auditTypes is empty', async () => { const onboardStartTime = Date.now() - 3600000; const testMessage = makeDisclaimerMessage(onboardStartTime, []); const testContext = makeDisclaimerContext([]); await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); - // Audit.allLatestForSite should not be called for disclaimer (auditTypes empty) + // No audit completion DB call when auditTypes is empty expect(testContext.dataAccess.Audit.allLatestForSite).to.not.have.been.called; + const disclaimerCalls = sayStub.args.map((a) => a[3]).filter(Boolean); + expect(disclaimerCalls.some((m) => m.includes('may still be in progress'))).to.be.false; + }); + + it('shows hourglass in opportunity status when source audit is pending', async () => { + const onboardStartTime = Date.now() - 3600000; + const testMessage = makeDisclaimerMessage(onboardStartTime, ['cwv']); + const staleAudit = { + getAuditType: () => 'cwv', + getAuditedAt: () => new Date(onboardStartTime - 1000).toISOString(), + }; + const cwvOpp = { + getType: sinon.stub().returns('cwv'), + getSuggestions: sinon.stub().resolves([{ id: 'sug-1' }]), + }; + const testContext = { + ...context, + dataAccess: { + Site: { + findById: sinon.stub().resolves({ + getOpportunities: sinon.stub().resolves([cwvOpp]), + getBaseURL: sinon.stub().returns('https://example.com'), + }), + }, + SiteTopPage: { + allBySiteIdAndSourceAndGeo: sinon.stub().resolves([]), + }, + Audit: { + allLatestForSite: sinon.stub().resolves([staleAudit]), + }, + }, + }; + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + // cwv audit is pending → opportunity shows ⏳, getSuggestions is NOT called + expect(cwvOpp.getSuggestions).to.not.have.been.called; + expect(sayStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.any, + sinon.match.any, + sinon.match(/Core Web Vitals :hourglass_flowing_sand:/), + ); }); it('falls back conservatively when Audit.allLatestForSite throws in disclaimer check', async () => { @@ -4325,5 +4377,27 @@ describe('Opportunity Status Processor', () => { sinon.match(/onboard status https:\/\/example\.com/), ); }); + + it('excludes infrastructure audit types not in AUDIT_OPPORTUNITY_MAP from disclaimer', async () => { + const onboardStartTime = Date.now() - 3600000; + // cwv is in the map; scrape-top-pages is not + const testMessage = makeDisclaimerMessage( + onboardStartTime, + ['cwv', 'scrape-top-pages'], + ); + const staleAudits = [ + { getAuditType: () => 'cwv', getAuditedAt: () => new Date(onboardStartTime - 500).toISOString() }, + { getAuditType: () => 'scrape-top-pages', getAuditedAt: () => new Date(onboardStartTime - 500).toISOString() }, + ]; + const testContext = makeDisclaimerContext(staleAudits); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + const calls = sayStub.args.map((a) => a[3]).filter(Boolean); + const disclaimer = calls.find((m) => m.includes('may still be in progress')); + expect(disclaimer).to.exist; + expect(disclaimer).to.include('Core Web Vitals'); + expect(disclaimer).to.not.include('Scrape Top Pages'); + }); }); }); From d726d4f8368df0064afd1e627462f0fb997d5acc Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Thu, 26 Mar 2026 10:58:39 -0500 Subject: [PATCH 4/8] refactor to use common lib --- package-lock.json | 1169 ++++++++++------- package.json | 4 +- .../audit-opportunity-map.js | 67 - .../opportunity-status-processor/handler.js | 37 +- .../opportunity-dependency-map.js | 38 - .../audit-opportunity-map.test.js | 176 --- .../opportunity-dependency-map.test.js | 77 -- .../opportunity-status-processor.test.js | 58 +- 8 files changed, 753 insertions(+), 873 deletions(-) delete mode 100644 src/tasks/opportunity-status-processor/audit-opportunity-map.js delete mode 100644 src/tasks/opportunity-status-processor/opportunity-dependency-map.js delete mode 100644 test/tasks/opportunity-status-processor/audit-opportunity-map.test.js delete mode 100644 test/tasks/opportunity-status-processor/opportunity-dependency-map.test.js diff --git a/package-lock.json b/package-lock.json index a086c3ea..d0ad5526 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "3.22.0", + "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/a882c8a25cbc82cb134ed75e831ef6bd/raw/89cfa44262f3d35bd008abf8240d05605d18cb71/adobe-spacecat-shared-data-access-3.31.0.tgz", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.6", "@adobe/spacecat-shared-gpt-client": "1.6.19", @@ -23,7 +23,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.9", "@adobe/spacecat-shared-scrape-client": "2.5.3", "@adobe/spacecat-shared-slack-client": "1.6.3", - "@adobe/spacecat-shared-utils": "1.102.1", + "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/fe9c5d8edb227d23339db99d0293bc81/raw/9483f68938ed65df6d3dab50cf55c4d5c4cff113/adobe-spacecat-shared-utils-1.106.0.tgz", "@adobe/spacecat-shared-vault-secrets": "1.3.0", "@aws-sdk/client-cloudwatch-logs": "3.1009.0", "@aws-sdk/client-lambda": "3.1009.0", @@ -544,6 +544,7 @@ "resolved": "https://registry.npmjs.org/@adobe/helix-universal/-/helix-universal-5.4.0.tgz", "integrity": "sha512-3ZfFdjYtpv7RCgul9yyOBsRVsxLNapwt0YjASBhyzJGNjnPxrWDlqDtbpBdwAgA1Nuh9nmjzFDFu8CJWv6BMKw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@adobe/fetch": "4.2.3", "aws4": "1.13.2" @@ -601,15 +602,15 @@ } }, "node_modules/@adobe/spacecat-shared-data-access": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-data-access/-/spacecat-shared-data-access-3.22.0.tgz", - "integrity": "sha512-38/b43ChYt5O08SpZfNhMbMDARlCLw7R71Aeccir4+s2DIZGkNs5XZpMhxJEabRDcwMDYJuzwyEzpHrEJJaaJg==", + "version": "3.31.0", + "resolved": "https://gist.github.com/tkotthakota-adobe/a882c8a25cbc82cb134ed75e831ef6bd/raw/89cfa44262f3d35bd008abf8240d05605d18cb71/adobe-spacecat-shared-data-access-3.31.0.tgz", + "integrity": "sha512-95iHJZA8LW+og3zX9hoQ/MZ1zSdHra3520Ey0CAnCUmDNu+sk1EP3AZrx7/fnUnbp1Ln7RRiqTafdi/fuL9pCg==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "^4.2.3", - "@adobe/spacecat-shared-utils": "1.101.0", + "@adobe/spacecat-shared-utils": "1.105.0", "@aws-sdk/client-s3": "^3.940.0", - "@supabase/postgrest-js": "2.99.1", + "@supabase/postgrest-js": "2.99.3", "@types/joi": "17.2.3", "aws-xray-sdk": "3.12.0", "joi": "18.0.2", @@ -1038,14 +1039,14 @@ } }, "node_modules/@adobe/spacecat-shared-data-access/node_modules/@adobe/spacecat-shared-utils": { - "version": "1.101.0", - "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-utils/-/spacecat-shared-utils-1.101.0.tgz", - "integrity": "sha512-b2JOlFieT6Q0+lvtkU/3SD2iV8pEEwpBaUvyNzbeR4uCgLBsYgvmrMJptpHD9pAqC+FOrePVjTZt2WhEKYo+aQ==", + "version": "1.105.0", + "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-utils/-/spacecat-shared-utils-1.105.0.tgz", + "integrity": "sha512-Jn/kXH9YVt7B0eqPUqzRZ+EgdSVF/IXrKgZ8qPD1thSy0lLYkbfYWh0LelIaPkqzvXRdRXdqYvQ1JOk0bA/n8A==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "4.2.3", - "@aws-sdk/client-s3": "3.1004.0", - "@aws-sdk/client-sqs": "3.1004.0", + "@aws-sdk/client-s3": "3.1009.0", + "@aws-sdk/client-sqs": "3.1009.0", "@json2csv/plainjs": "7.0.6", "aws-xray-sdk": "3.12.0", "cheerio": "1.2.0", @@ -1062,243 +1063,6 @@ "npm": ">=10.9.0 <12.0.0" } }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/client-s3": { - "version": "3.1004.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1004.0.tgz", - "integrity": "sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.18", - "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", - "@aws-sdk/middleware-expect-continue": "^3.972.7", - "@aws-sdk/middleware-flexible-checksums": "^3.973.4", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-location-constraint": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-sdk-s3": "^3.972.18", - "@aws-sdk/middleware-ssec": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.19", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/signature-v4-multi-region": "^3.996.6", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.4", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/eventstream-serde-browser": "^4.2.11", - "@smithy/eventstream-serde-config-resolver": "^4.3.11", - "@smithy/eventstream-serde-node": "^4.2.11", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-blob-browser": "^4.2.12", - "@smithy/hash-node": "^4.2.11", - "@smithy/hash-stream-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/md5-js": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-base64": "^4.3.2", - "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-stream": "^4.5.17", - "@smithy/util-utf8": "^4.2.2", - "@smithy/util-waiter": "^4.2.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/client-sqs": { - "version": "3.1004.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1004.0.tgz", - "integrity": "sha512-aCREPa+SyOE6pD2JuD32E6HJAX9ik+qyXtyXPsTzPL5hDvCk85ccZUwSFpk1ErxubB4v832IDukvxfXOclQtzA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.18", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-sdk-sqs": "^3.972.13", - "@aws-sdk/middleware-user-agent": "^3.972.19", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.4", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/md5-js": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-base64": "^4.3.2", - "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-utf8": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", - "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/protocol-http": "^5.3.12", - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", - "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", - "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.12", - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.972.15", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.15.tgz", - "integrity": "sha512-X7yt+gJzZEK247nppuUVWS1i83q8zhZdBk1H2b6/qeXNv1ILgw0bQLNbFNG4gJi3P7vZV+PhtPkax0nwXAvRtg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/smithy-client": "^4.12.5", - "@smithy/types": "^4.13.1", - "@smithy/util-hex-encoding": "^4.2.2", - "@smithy/util-utf8": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.8.tgz", - "integrity": "sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/config-resolver": "^4.4.11", - "@smithy/node-config-provider": "^4.3.12", - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/types": { - "version": "3.973.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", - "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.13.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", - "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/types": "^4.13.1", - "@smithy/url-parser": "^4.2.12", - "@smithy/util-endpoints": "^3.3.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@adobe/spacecat-shared-data-access/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", - "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.6", - "@smithy/types": "^4.13.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, "node_modules/@adobe/spacecat-shared-google-client": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-google-client/-/spacecat-shared-google-client-1.5.6.tgz", @@ -4129,172 +3893,634 @@ "integrity": "sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.940.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.940.0.tgz", + "integrity": "sha512-JYkLjgS1wLoKHJ40G63+afM1ehmsPsjcmrHirKh8+kSCx4ip7+nL1e/twV4Zicxr8RJi9Y0Ahq5mDvneilDDKQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.940.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.18.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.8", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-ssec": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.936.0.tgz", + "integrity": "sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.940.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.940.0.tgz", + "integrity": "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.940.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.940.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.940.0.tgz", + "integrity": "sha512-ugHZEoktD/bG6mdgmhzLDjMP2VrYRAUPRPF1DpCyiZexkH7DCU7XrSJyXMvkcf0DHV+URk0q2sLf/oqn1D2uYw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.940.0", + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.940.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.940.0.tgz", + "integrity": "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.940.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@adobe/spacecat-shared-slack-client/node_modules/cheerio": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.12.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/@adobe/spacecat-shared-utils": { + "version": "1.106.0", + "resolved": "https://gist.github.com/tkotthakota-adobe/fe9c5d8edb227d23339db99d0293bc81/raw/9483f68938ed65df6d3dab50cf55c4d5c4cff113/adobe-spacecat-shared-utils-1.106.0.tgz", + "integrity": "sha512-lL1kuVKobOT0jvIKSipoxRSLJK18CmZqD1CUob/eftEY3CrbYmqIT1VEWX2M1Ytoe1SNhVvhiAiW02mOm1Oi4Q==", + "license": "Apache-2.0", + "dependencies": { + "@adobe/fetch": "4.2.3", + "@aws-sdk/client-s3": "3.1014.0", + "@aws-sdk/client-sqs": "3.1014.0", + "@json2csv/plainjs": "7.0.6", + "aws-xray-sdk": "3.12.0", + "cheerio": "1.2.0", + "date-fns": "4.1.0", + "franc-min": "6.2.0", + "iso-639-3": "3.0.1", + "urijs": "1.19.11", + "validator": "^13.15.15", + "world-countries": "5.1.0", + "zod": "^4.1.11" + }, + "engines": { + "node": ">=22.0.0 <25.0.0", + "npm": ">=10.9.0 <12.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/client-s3": { + "version": "3.1014.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1014.0.tgz", + "integrity": "sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.23", + "@aws-sdk/credential-provider-node": "^3.972.24", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", + "@aws-sdk/middleware-expect-continue": "^3.972.8", + "@aws-sdk/middleware-flexible-checksums": "^3.974.3", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-location-constraint": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-s3": "^3.972.23", + "@aws-sdk/middleware-ssec": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.24", + "@aws-sdk/region-config-resolver": "^3.972.9", + "@aws-sdk/signature-v4-multi-region": "^3.996.11", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.10", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-blob-browser": "^4.2.13", + "@smithy/hash-node": "^4.2.12", + "@smithy/hash-stream-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-stream": "^4.5.20", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/client-sqs": { + "version": "3.1014.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1014.0.tgz", + "integrity": "sha512-xZ/yAd5FtpDyeIRRSXlRV7/PC0wC3vUENBEx5h/j06UkrOosoELp7YguC7ecwvKYeO7mvOO4I94iEhfJspp7Dw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.23", + "@aws-sdk/credential-provider-node": "^3.972.24", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-sdk-sqs": "^3.972.17", + "@aws-sdk/middleware-user-agent": "^3.972.24", + "@aws-sdk/region-config-resolver": "^3.972.9", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.10", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.22.tgz", + "integrity": "sha512-cXp0VTDWT76p3hyK5D51yIKEfpf6/zsUvMfaB8CkyqadJxMQ8SbEeVroregmDlZbtG31wkj9ei0WnftmieggLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.24.tgz", + "integrity": "sha512-h694K7+tRuepSRJr09wTvQfaEnjzsKZ5s7fbESrVds02GT/QzViJ94/HCNwM7bUfFxqpPXHxulZfL6Cou0dwPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/types": "^3.973.6", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.20", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.24.tgz", + "integrity": "sha512-O46fFmv0RDFWiWEA9/e6oW92BnsyAXuEgTTasxHligjn2RCr9L/DK773m/NoFaL3ZdNAUz8WxgxunleMnHAkeQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/credential-provider-env": "^3.972.22", + "@aws-sdk/credential-provider-http": "^3.972.24", + "@aws-sdk/credential-provider-login": "^3.972.24", + "@aws-sdk/credential-provider-process": "^3.972.22", + "@aws-sdk/credential-provider-sso": "^3.972.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.24", + "@aws-sdk/nested-clients": "^3.996.14", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.24.tgz", + "integrity": "sha512-sIk8oa6AzDoUhxsR11svZESqvzGuXesw62Rl2oW6wguZx8i9cdGCvkFg+h5K7iucUZP8wyWibUbJMc+J66cu5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/nested-clients": "^3.996.14", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.25.tgz", + "integrity": "sha512-m7dR0Dsva2P+VUpL+VkC0WwiDby5pgmWXkRVDB5rlwv0jXJrQJf7YMtCoM8Wjk0H9jPeCYOxOXXcIgp/qp5Alg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.22", + "@aws-sdk/credential-provider-http": "^3.972.24", + "@aws-sdk/credential-provider-ini": "^3.972.24", + "@aws-sdk/credential-provider-process": "^3.972.22", + "@aws-sdk/credential-provider-sso": "^3.972.24", + "@aws-sdk/credential-provider-web-identity": "^3.972.24", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.22.tgz", + "integrity": "sha512-Os32s8/4gTZjBk5BtoS/cuTILaj+K72d0dVG7TCJX/fC4598cxwLDmf1AEHEpER5oL3K//yETjvFaz0V8oO5Xw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.24.tgz", + "integrity": "sha512-PaFv7snEfypU2yXkpvfyWgddEbDLtgVe51wdZlinhc2doubBjUzJZZpgwuF2Jenl1FBydMhNpMjD6SBUM3qdSA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/nested-clients": "^3.996.14", + "@aws-sdk/token-providers": "3.1015.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.24.tgz", + "integrity": "sha512-J6H4R1nvr3uBTqD/EeIPAskrBtET4WFfNhpFySr2xW7bVZOXpQfPjrLSIx65jcNjBmLXzWq8QFLdVoGxiGG/SA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/nested-clients": "^3.996.14", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.940.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.940.0.tgz", - "integrity": "sha512-JYkLjgS1wLoKHJ40G63+afM1ehmsPsjcmrHirKh8+kSCx4ip7+nL1e/twV4Zicxr8RJi9Y0Ahq5mDvneilDDKQ==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", + "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.940.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.18.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/signature-v4": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-stream": "^4.5.6", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/types": "^3.973.6", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-ssec": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.936.0.tgz", - "integrity": "sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.17.tgz", + "integrity": "sha512-LnzPRRoDXGtlFV2G1p2rsY6fRKrbf6Pvvc21KliSLw3+NmQca2+Aa1QIMRbpQvZYedsSqkGYwxe+qvXwQ2uxDw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.940.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.940.0.tgz", - "integrity": "sha512-nJbLrUj6fY+l2W2rIB9P4Qvpiy0tnTdg/dmixRxrU1z3e8wBdspJlyE+AZN4fuVbeL6rrRrO/zxQC1bB3cw5IA==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/nested-clients": { + "version": "3.996.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.14.tgz", + "integrity": "sha512-fSESKvh1VbfjtV3QMnRkCPZWkUbQof6T/DOpiLp33yP2wA+rbwwnZeG3XT3Ekljgw2I8X4XaQPnw+zSR8yxJ5Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.940.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-endpoints": "3.936.0", - "@smithy/core": "^3.18.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.25", + "@aws-sdk/region-config-resolver": "^3.972.9", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.11", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.27", + "@smithy/middleware-retry": "^4.4.44", + "@smithy/middleware-serde": "^4.2.15", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.0", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.43", + "@smithy/util-defaults-mode-node": "^4.2.47", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.940.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.940.0.tgz", - "integrity": "sha512-ugHZEoktD/bG6mdgmhzLDjMP2VrYRAUPRPF1DpCyiZexkH7DCU7XrSJyXMvkcf0DHV+URk0q2sLf/oqn1D2uYw==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.9.tgz", + "integrity": "sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.940.0", - "@aws-sdk/types": "3.936.0", - "@smithy/protocol-http": "^5.3.5", - "@smithy/signature-v4": "^5.3.5", - "@smithy/types": "^4.9.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/config-resolver": "^4.4.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/util-arn-parser": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", - "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/token-providers": { + "version": "3.1015.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1015.0.tgz", + "integrity": "sha512-3OSD4y110nisRhHzFOjoEeHU4GQL4KpzkX9PxzWaiZe0Yg2+thZKM0Pn9DjYwezH5JYfh/K++xK/SE0IHGrmCQ==", "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "^3.973.24", + "@aws-sdk/nested-clients": "^3.996.14", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.940.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.940.0.tgz", - "integrity": "sha512-dlD/F+L/jN26I8Zg5x0oDGJiA+/WEQmnSE27fi5ydvYnpfQLwThtQo9SsNS47XSR/SOULaaoC9qx929rZuo74A==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/types": { + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", + "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.940.0", - "@aws-sdk/types": "3.936.0", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-slack-client/node_modules/cheerio": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", - "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", - "license": "MIT", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", + "license": "Apache-2.0", "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.1", - "htmlparser2": "^10.0.0", - "parse5": "^7.3.0", - "parse5-htmlparser2-tree-adapter": "^7.1.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^7.12.0", - "whatwg-mimetype": "^4.0.0" + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", + "tslib": "^2.6.2" }, "engines": { - "node": ">=20.18.1" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + "node": ">=20.0.0" } }, - "node_modules/@adobe/spacecat-shared-utils": { - "version": "1.102.1", - "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-utils/-/spacecat-shared-utils-1.102.1.tgz", - "integrity": "sha512-rHQDQuF06tMYtNpLdvdOGl3ts5KsRYpPUzi1xyG/QOI6yKPTwV6WCezMeXlq33YCac9AdPtfTH+s8rxX/0CzCw==", + "node_modules/@adobe/spacecat-shared-utils/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", + "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", "license": "Apache-2.0", "dependencies": { - "@adobe/fetch": "4.2.3", - "@aws-sdk/client-s3": "3.1009.0", - "@aws-sdk/client-sqs": "3.1009.0", - "@json2csv/plainjs": "7.0.6", - "aws-xray-sdk": "3.12.0", - "cheerio": "1.2.0", - "date-fns": "4.1.0", - "franc-min": "6.2.0", - "iso-639-3": "3.0.1", - "urijs": "1.19.11", - "validator": "^13.15.15", - "world-countries": "5.1.0", - "zod": "^4.1.11" - }, - "engines": { - "node": ">=22.0.0 <25.0.0", - "npm": ">=10.9.0 <12.0.0" + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" } }, "node_modules/@adobe/spacecat-shared-vault-secrets": { @@ -4843,6 +5069,7 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.940.0.tgz", "integrity": "sha512-u2sXsNJazJbuHeWICvsj6RvNyJh3isedEfPvB21jK/kxcriK+dE/izlKC2cyxUjERCmku0zTFNzY9FhrLbYHjQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -6064,19 +6291,19 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.20.tgz", - "integrity": "sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA==", + "version": "3.973.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.24.tgz", + "integrity": "sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.973.6", - "@aws-sdk/xml-builder": "^3.972.11", - "@smithy/core": "^3.23.11", + "@aws-sdk/xml-builder": "^3.972.15", + "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", @@ -6101,13 +6328,13 @@ } }, "node_modules/@aws-sdk/core/node_modules/@aws-sdk/xml-builder": { - "version": "3.972.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.11.tgz", - "integrity": "sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ==", + "version": "3.972.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.15.tgz", + "integrity": "sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==", "license": "Apache-2.0", "dependencies": { "@smithy/types": "^4.13.1", - "fast-xml-parser": "5.4.1", + "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" }, "engines": { @@ -6115,9 +6342,9 @@ } }, "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", - "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", "funding": [ { "type": "github", @@ -6126,8 +6353,9 @@ ], "license": "MIT", "dependencies": { - "fast-xml-builder": "^1.0.0", - "strnum": "^2.1.2" + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -6910,15 +7138,15 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.973.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.973.6.tgz", - "integrity": "sha512-0nYEgkJH7Yt9k+nZJyllTghnkKaz17TWFcr5Mi0XMVMzYlF4ytDZADQpF2/iJo36cKL5AYSzRsvlykE4M/ErTA==", + "version": "3.974.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.4.tgz", + "integrity": "sha512-fhCbZXPAyy8btnNbnBlR7Cc1nD54cETSvGn2wey71ehsM89AKPO8Dpco9DBAAgvrUdLrdHQepBXcyX4vxC5OwA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.24", "@aws-sdk/crc64-nvme": "^3.972.5", "@aws-sdk/types": "^3.973.6", "@smithy/is-array-buffer": "^4.2.2", @@ -6926,7 +7154,7 @@ "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -7020,23 +7248,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.20.tgz", - "integrity": "sha512-yhva/xL5H4tWQgsBjwV+RRD0ByCzg0TcByDCLp3GXdn/wlyRNfy8zsswDtCvr1WSKQkSQYlyEzPuWkJG0f5HvQ==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.25.tgz", + "integrity": "sha512-4xJL7O+XkhbSkT4yAYshkAww+mxJvtGQneNHH0MOpe+w8Vo2z87M9z06UO3G6zPM2c3Ef2yKczvZpTgdArMHfg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.24", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-arn-parser": "^3.972.3", - "@smithy/core": "^3.23.11", + "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.12", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, @@ -7102,15 +7330,15 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.21.tgz", - "integrity": "sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==", + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.25.tgz", + "integrity": "sha512-QxiMPofvOt8SwSynTOmuZfvvPM1S9QfkESBxB22NMHTRXCJhR5BygLl8IXfC4jELiisQgwsgUby21GtXfX3f/g==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.973.20", + "@aws-sdk/core": "^3.973.24", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", - "@smithy/core": "^3.23.11", + "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.12", @@ -7281,12 +7509,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.8.tgz", - "integrity": "sha512-n1qYFD+tbqZuyskVaxUE+t10AUz9g3qzDw3Tp6QZDKmqsjfDmZBd4GIk2EKJJNtcCBtE5YiUjDYA+3djFAFBBg==", + "version": "3.996.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.13.tgz", + "integrity": "sha512-7j8rOFHHq4e9McCSuWBmBSADriW5CjPUem4inckRh/cyQGaijBwDbkNbVTgDVDWqFo29SoVVUfI6HCOnck6HZw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "^3.972.20", + "@aws-sdk/middleware-sdk-s3": "^3.972.25", "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", @@ -7433,12 +7661,12 @@ } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.7.tgz", - "integrity": "sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==", + "version": "3.973.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.11.tgz", + "integrity": "sha512-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.21", + "@aws-sdk/middleware-user-agent": "^3.972.25", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", @@ -8833,6 +9061,7 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -9004,6 +9233,7 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -10309,9 +10539,9 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", - "integrity": "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", + "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.12", @@ -10326,9 +10556,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.23.11", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.11.tgz", - "integrity": "sha512-952rGf7hBRnhUIaeLp6q4MptKW8sPFe5VvkoZ5qIzFAtx6c/QZ/54FS3yootsyUSf9gJX/NBqEBNdNR7jMIlpQ==", + "version": "3.23.12", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.12.tgz", + "integrity": "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==", "license": "Apache-2.0", "dependencies": { "@smithy/protocol-http": "^5.3.12", @@ -10337,7 +10567,7 @@ "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" @@ -10546,13 +10776,13 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.25", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.25.tgz", - "integrity": "sha512-dqjLwZs2eBxIUG6Qtw8/YZ4DvzHGIf0DA18wrgtfP6a50UIO7e2nY0FPdcbv5tVJKqWCCU5BmGMOUwT7Puan+A==", + "version": "4.4.27", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.27.tgz", + "integrity": "sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", - "@smithy/middleware-serde": "^4.2.14", + "@smithy/core": "^3.23.12", + "@smithy/middleware-serde": "^4.2.15", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", @@ -10565,15 +10795,15 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.42", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.42.tgz", - "integrity": "sha512-vbwyqHRIpIZutNXZpLAozakzamcINaRCpEy1MYmK6xBeW3xN+TyPRA123GjXnuxZIjc9848MRRCugVMTXxC4Eg==", + "version": "4.4.44", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.44.tgz", + "integrity": "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==", "license": "Apache-2.0", "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", @@ -10585,12 +10815,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.14.tgz", - "integrity": "sha512-+CcaLoLa5apzSRtloOyG7lQvkUw2ZDml3hRh4QiG9WyEPfW5Ke/3tPOPiPjUneuT59Tpn8+c3RVaUvvkkwqZwg==", + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.15.tgz", + "integrity": "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", + "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" @@ -10628,9 +10858,9 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.16", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.16.tgz", - "integrity": "sha512-ULC8UCS/HivdCB3jhi+kLFYe4B5gxH2gi9vHBfEIiRrT2jfKiZNiETJSlzRtE6B26XbBHjPtc8iZKSNqMol9bw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.0.tgz", + "integrity": "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==", "license": "Apache-2.0", "dependencies": { "@smithy/abort-controller": "^4.2.12", @@ -10741,17 +10971,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.5.tgz", - "integrity": "sha512-UqwYawyqSr/aog8mnLnfbPurS0gi4G7IYDcD28cUIBhsvWs1+rQcL2IwkUQ+QZ7dibaoRzhNF99fAQ9AUcO00w==", + "version": "4.12.7", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.7.tgz", + "integrity": "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.23.11", - "@smithy/middleware-endpoint": "^4.4.25", + "@smithy/core": "^3.23.12", + "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", - "@smithy/util-stream": "^4.5.19", + "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" }, "engines": { @@ -10848,13 +11078,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.41", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.41.tgz", - "integrity": "sha512-M1w1Ux0rSVvBOxIIiqbxvZvhnjQ+VUjJrugtORE90BbadSTH+jsQL279KRL3Hv0w69rE7EuYkV/4Lepz/NBW9g==", + "version": "4.3.43", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.43.tgz", + "integrity": "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==", "license": "Apache-2.0", "dependencies": { "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -10863,16 +11093,16 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.44", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.44.tgz", - "integrity": "sha512-YPze3/lD1KmWuZsl9JlfhcgGLX7AXhSoaCDtiPntUjNW5/YY0lOHjkcgxyE9x/h5vvS1fzDifMGjzqnNlNiqOQ==", + "version": "4.2.47", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.47.tgz", + "integrity": "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.11", + "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", - "@smithy/smithy-client": "^4.12.5", + "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, @@ -10934,13 +11164,13 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.19", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.19.tgz", - "integrity": "sha512-v4sa+3xTweL1CLO2UP0p7tvIMH/Rq1X4KKOxd568mpe6LSLMQCnDHs4uv7m3ukpl3HvcN2JH6jiCS0SNRXKP/w==", + "version": "4.5.20", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.20.tgz", + "integrity": "sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==", "license": "Apache-2.0", "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", - "@smithy/node-http-handler": "^4.4.16", + "@smithy/node-http-handler": "^4.5.0", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", @@ -11016,9 +11246,9 @@ "license": "MIT" }, "node_modules/@supabase/postgrest-js": { - "version": "2.99.1", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.99.1.tgz", - "integrity": "sha512-gtw2ibJrADvfqrpUWXGNlrYUvxttF4WVWfPpTFKOb2IRj7B6YRWMDgcrYqIuD4ZEabK4m6YKQCCGy6clgf1lPA==", + "version": "2.99.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.99.3.tgz", + "integrity": "sha512-8HxEf+zNycj7Z8+ONhhlu+7J7Ha+L6weyCtdEeK2mN5OWJbh6n4LPU4iuJ5UlCvvNnbSXMoutY7piITEEAgl2g==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -11340,6 +11570,7 @@ "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", @@ -11610,6 +11841,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -11657,6 +11889,7 @@ "integrity": "sha512-F+LMD2IDIXuHxgpLJh3nkLj9+tSaEzoUWd+7fONGq5pe2169FUDjpEkOfEpoGLz1sbZni/69p07OsecNfAOpqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12109,6 +12342,7 @@ "resolved": "https://registry.npmjs.org/aws-xray-sdk-core/-/aws-xray-sdk-core-3.12.0.tgz", "integrity": "sha512-lwalRdxXRy+Sn49/vN7W507qqmBRk5Fy2o0a9U6XTjL9IV+oR5PUiiptoBrOcaYCiVuGld8OEbNqhm6wvV3m6A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@aws-sdk/types": "^3.4.1", "@smithy/service-error-classification": "^2.0.4", @@ -12710,6 +12944,7 @@ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -14623,6 +14858,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -15225,16 +15461,19 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", - "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } }, "node_modules/fast-xml-parser": { "version": "5.2.5", @@ -18168,6 +18407,7 @@ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -18436,6 +18676,7 @@ "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "dev": true, "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -21200,6 +21441,7 @@ "dev": true, "inBundle": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -22135,6 +22377,21 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", + "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -22752,6 +23009,7 @@ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -22762,6 +23020,7 @@ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -23360,6 +23619,7 @@ "integrity": "sha512-WRgl5GcypwramYX4HV+eQGzUbD7UUbljVmS+5G1uMwX/wLgYuJAxGeerXJDMO2xshng4+FXqCgyB5QfClV6WjA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/error": "^4.0.0", @@ -24056,6 +24316,7 @@ "integrity": "sha512-VHV4UaoxIe5jrMd89Y9duI76T5g3Lp+ET+ctLhLDaZtSznDPah1KKpRElbdBV4RwqWSw2vadFiVs9Del7MbVeQ==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.1", "@sinonjs/fake-timers": "^15.1.1", @@ -24549,9 +24810,9 @@ } }, "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", "funding": [ { "type": "github", @@ -24573,6 +24834,7 @@ "integrity": "sha512-J72R4ltw0UBVUlEjTzI0gg2STOqlI9JBhQOL4Dxt7aJOnnSesy0qJDn4PYfMCafk9cWOaVg129Pesl5o+DIh0Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@emotion/is-prop-valid": "1.4.0", "@emotion/unitless": "0.10.0", @@ -25448,6 +25710,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 8c9f16e5..56a7a9e3 100755 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "3.22.0", + "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/a882c8a25cbc82cb134ed75e831ef6bd/raw/89cfa44262f3d35bd008abf8240d05605d18cb71/adobe-spacecat-shared-data-access-3.31.0.tgz", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.6", "@adobe/spacecat-shared-gpt-client": "1.6.19", @@ -85,7 +85,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.9", "@adobe/spacecat-shared-scrape-client": "2.5.3", "@adobe/spacecat-shared-slack-client": "1.6.3", - "@adobe/spacecat-shared-utils": "1.102.1", + "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/fe9c5d8edb227d23339db99d0293bc81/raw/9483f68938ed65df6d3dab50cf55c4d5c4cff113/adobe-spacecat-shared-utils-1.106.0.tgz", "@aws-sdk/client-lambda": "3.1009.0", "@aws-sdk/client-sqs": "3.1009.0", "@aws-sdk/client-s3": "3.1009.0", diff --git a/src/tasks/opportunity-status-processor/audit-opportunity-map.js b/src/tasks/opportunity-status-processor/audit-opportunity-map.js deleted file mode 100644 index f937a17b..00000000 --- a/src/tasks/opportunity-status-processor/audit-opportunity-map.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2025 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -/** - * Maps audit types to their corresponding opportunity types - * This represents the superset of opportunities that can be generated from each audit - * - * Key: Audit type - * Value: Array of opportunity types that can be generated from this audit - */ -export const AUDIT_OPPORTUNITY_MAP = { - cwv: ['cwv'], - 'forms-opportunities': ['form-accessibility', 'forms-opportunities'], - 'meta-tags': ['meta-tags'], - 'experimentation-opportunities': ['high-organic-low-ctr'], - 'broken-backlinks': ['broken-backlinks'], - 'broken-internal-links': ['broken-internal-links'], - sitemap: ['sitemap'], - 'alt-text': ['alt-text'], - accessibility: ['accessibility'], -}; - -/** - * Gets all opportunity types for a given audit type - * @param {string} auditType - The audit type - * @returns {Array} Array of opportunity types, or empty array if audit type not found - */ -export function getOpportunitiesForAudit(auditType) { - return AUDIT_OPPORTUNITY_MAP[auditType] || []; -} - -/** - * Gets all audit types that can generate a specific opportunity type - * @param {string} opportunityType - The opportunity type - * @returns {Array} Array of audit types that can generate this opportunity - */ -export function getAuditsForOpportunity(opportunityType) { - return Object.entries(AUDIT_OPPORTUNITY_MAP) - .filter(([, opportunities]) => opportunities.includes(opportunityType)) - .map(([auditType]) => auditType); -} - -/** - * Gets all unique opportunity types across all audits - * @returns {Array} Array of all unique opportunity types - */ -export function getAllOpportunityTypes() { - const allOpportunities = Object.values(AUDIT_OPPORTUNITY_MAP).flat(); - return [...new Set(allOpportunities)]; -} - -/** - * Gets all audit types defined in the map - * @returns {Array} Array of all audit types - */ -export function getAllAuditTypes() { - return Object.keys(AUDIT_OPPORTUNITY_MAP); -} diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index efbe5530..7153072c 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -14,12 +14,16 @@ import { ok } from '@adobe/spacecat-shared-http-utils'; import RUMAPIClient from '@adobe/spacecat-shared-rum-api-client'; import GoogleClient from '@adobe/spacecat-shared-google-client'; import { ScrapeClient } from '@adobe/spacecat-shared-scrape-client'; -import { resolveCanonicalUrl } from '@adobe/spacecat-shared-utils'; +import { + resolveCanonicalUrl, + getAuditsForOpportunity, + getOpportunityTitle, + OPPORTUNITY_DEPENDENCY_MAP, + getOpportunitiesForAudit, +} from '@adobe/spacecat-shared-utils'; import { getAuditStatus } from '../../utils/cloudwatch-utils.js'; import { checkAndAlertBotProtection } from '../../utils/bot-detection.js'; import { say } from '../../utils/slack-utils.js'; -import { getOpportunitiesForAudit, getAuditsForOpportunity } from './audit-opportunity-map.js'; -import { OPPORTUNITY_DEPENDENCY_MAP } from './opportunity-dependency-map.js'; const TASK_TYPE = 'opportunity-status-processor'; @@ -94,33 +98,6 @@ async function isGSCConfigured(siteUrl, context) { } } -/** - * Gets the opportunity title from the opportunity type - * @param {string} opportunityType - The opportunity type - * @returns {string} The opportunity title - */ -function getOpportunityTitle(opportunityType) { - const opportunityTitles = { - cwv: 'Core Web Vitals', - 'meta-tags': 'SEO Meta Tags', - 'broken-backlinks': 'Broken Backlinks', - 'broken-internal-links': 'Broken Internal Links', - 'alt-text': 'Alt Text', - sitemap: 'Sitemap', - }; - - // Check if the opportunity type exists in our map - if (opportunityTitles[opportunityType]) { - return opportunityTitles[opportunityType]; - } - - // Convert kebab-case to Title Case (e.g., "first-second" -> "First Second") - return opportunityType - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - /** * Filters scrape jobs to only include those created after onboardStartTime * This ensures we only check jobs from the CURRENT onboarding session, diff --git a/src/tasks/opportunity-status-processor/opportunity-dependency-map.js b/src/tasks/opportunity-status-processor/opportunity-dependency-map.js deleted file mode 100644 index efd03bd8..00000000 --- a/src/tasks/opportunity-status-processor/opportunity-dependency-map.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2025 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -/** - * Maps opportunity types to their required dependencies - * Dependencies can be data sources (RUM, AHREFSImport, GSC, scraping) - * - * Key: Opportunity type - * Value: Array of required dependencies for this opportunity to be generated - */ -export const OPPORTUNITY_DEPENDENCY_MAP = { - cwv: ['RUM'], - 'high-organic-low-ctr': ['RUM'], - 'broken-internal-links': ['RUM', 'AHREFSImport'], - 'meta-tags': ['AHREFSImport', 'scraping'], // meta-tags audit uses scraping - 'broken-backlinks': ['AHREFSImport', 'scraping'], // broken-backlinks audit uses scraping - 'alt-text': ['AHREFSImport', 'scraping'], // alt-text audit uses scraping - 'form-accessibility': ['RUM', 'scraping'], // forms audit uses scraping - 'forms-opportunities': ['RUM', 'scraping'], // forms audit uses scraping -}; - -/** - * Gets all dependencies for a given opportunity type - * @param {string} opportunityType - The opportunity type - * @returns {Array} Array of dependency names, or empty array if no dependencies - */ -export function getDependenciesForOpportunity(opportunityType) { - return OPPORTUNITY_DEPENDENCY_MAP[opportunityType] || []; -} diff --git a/test/tasks/opportunity-status-processor/audit-opportunity-map.test.js b/test/tasks/opportunity-status-processor/audit-opportunity-map.test.js deleted file mode 100644 index c1de92c4..00000000 --- a/test/tasks/opportunity-status-processor/audit-opportunity-map.test.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2025 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -/* eslint-env mocha */ - -import { expect } from 'chai'; -import { - AUDIT_OPPORTUNITY_MAP, - getOpportunitiesForAudit, - getAllAuditTypes, - getAuditsForOpportunity, - getAllOpportunityTypes, -} from '../../../src/tasks/opportunity-status-processor/audit-opportunity-map.js'; - -describe('Audit Opportunity Map', () => { - describe('AUDIT_OPPORTUNITY_MAP', () => { - it('should contain all expected audit types', () => { - expect(AUDIT_OPPORTUNITY_MAP).to.be.an('object'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('cwv'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('forms-opportunities'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('meta-tags'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('experimentation-opportunities'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('broken-backlinks'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('broken-internal-links'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('sitemap'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('alt-text'); - expect(AUDIT_OPPORTUNITY_MAP).to.have.property('accessibility'); - }); - - it('should map audits to opportunities correctly', () => { - expect(AUDIT_OPPORTUNITY_MAP.cwv).to.deep.equal(['cwv']); - expect(AUDIT_OPPORTUNITY_MAP['forms-opportunities']).to.deep.equal([ - 'form-accessibility', - 'forms-opportunities', - ]); - expect(AUDIT_OPPORTUNITY_MAP['meta-tags']).to.deep.equal(['meta-tags']); - expect(AUDIT_OPPORTUNITY_MAP['experimentation-opportunities']).to.deep.equal([ - 'high-organic-low-ctr', - ]); - }); - }); - - describe('getOpportunitiesForAudit', () => { - it('should return opportunities for known audit type', () => { - const opportunities = getOpportunitiesForAudit('cwv'); - expect(opportunities).to.be.an('array'); - expect(opportunities).to.deep.equal(['cwv']); - }); - - it('should return multiple opportunities for forms audit', () => { - const opportunities = getOpportunitiesForAudit('forms-opportunities'); - expect(opportunities).to.be.an('array'); - expect(opportunities).to.have.lengthOf(2); - expect(opportunities).to.include('form-accessibility'); - expect(opportunities).to.include('forms-opportunities'); - }); - - it('should return empty array for unknown audit type', () => { - const opportunities = getOpportunitiesForAudit('unknown-audit'); - expect(opportunities).to.be.an('array'); - expect(opportunities).to.have.lengthOf(0); - }); - - it('should return empty array for null audit type', () => { - const opportunities = getOpportunitiesForAudit(null); - expect(opportunities).to.be.an('array'); - expect(opportunities).to.have.lengthOf(0); - }); - - it('should return empty array for undefined audit type', () => { - const opportunities = getOpportunitiesForAudit(undefined); - expect(opportunities).to.be.an('array'); - expect(opportunities).to.have.lengthOf(0); - }); - - it('should handle all defined audit types', () => { - const auditTypes = [ - 'cwv', - 'forms-opportunities', - 'meta-tags', - 'experimentation-opportunities', - 'broken-backlinks', - 'broken-internal-links', - 'sitemap', - 'alt-text', - 'accessibility', - ]; - - auditTypes.forEach((auditType) => { - const opportunities = getOpportunitiesForAudit(auditType); - expect(opportunities).to.be.an('array'); - expect(opportunities.length).to.be.greaterThan(0); - }); - }); - }); - - describe('getAllAuditTypes', () => { - it('should return all audit types', () => { - const auditTypes = getAllAuditTypes(); - expect(auditTypes).to.be.an('array'); - expect(auditTypes).to.have.lengthOf(9); - }); - - it('should include all expected audit types', () => { - const auditTypes = getAllAuditTypes(); - expect(auditTypes).to.include('cwv'); - expect(auditTypes).to.include('forms-opportunities'); - expect(auditTypes).to.include('meta-tags'); - expect(auditTypes).to.include('experimentation-opportunities'); - expect(auditTypes).to.include('broken-backlinks'); - expect(auditTypes).to.include('broken-internal-links'); - expect(auditTypes).to.include('sitemap'); - expect(auditTypes).to.include('alt-text'); - expect(auditTypes).to.include('accessibility'); - }); - }); -}); - -describe('Audit Opportunity Map - Additional Coverage', () => { - describe('getAuditsForOpportunity', () => { - it('should return audits that generate cwv opportunity', () => { - const audits = getAuditsForOpportunity('cwv'); - expect(audits).to.be.an('array'); - expect(audits).to.deep.equal(['cwv']); - }); - - it('should return multiple audits for form-accessibility opportunity', () => { - const audits = getAuditsForOpportunity('form-accessibility'); - expect(audits).to.be.an('array'); - expect(audits).to.include('forms-opportunities'); - }); - - it('should return empty array for unknown opportunity', () => { - const audits = getAuditsForOpportunity('unknown-opportunity'); - expect(audits).to.be.an('array'); - expect(audits).to.have.lengthOf(0); - }); - - it('should return audits for high-organic-low-ctr opportunity', () => { - const audits = getAuditsForOpportunity('high-organic-low-ctr'); - expect(audits).to.be.an('array'); - expect(audits).to.deep.equal(['experimentation-opportunities']); - }); - }); - - describe('getAllOpportunityTypes', () => { - it('should return all unique opportunity types', () => { - const opportunities = getAllOpportunityTypes(); - expect(opportunities).to.be.an('array'); - expect(opportunities.length).to.be.greaterThan(0); - }); - - it('should not contain duplicates', () => { - const opportunities = getAllOpportunityTypes(); - const uniqueOpportunities = [...new Set(opportunities)]; - expect(opportunities).to.have.lengthOf(uniqueOpportunities.length); - }); - - it('should include all defined opportunity types', () => { - const opportunities = getAllOpportunityTypes(); - expect(opportunities).to.include('cwv'); - expect(opportunities).to.include('meta-tags'); - expect(opportunities).to.include('broken-backlinks'); - expect(opportunities).to.include('high-organic-low-ctr'); - }); - }); -}); diff --git a/test/tasks/opportunity-status-processor/opportunity-dependency-map.test.js b/test/tasks/opportunity-status-processor/opportunity-dependency-map.test.js deleted file mode 100644 index f86cd6ed..00000000 --- a/test/tasks/opportunity-status-processor/opportunity-dependency-map.test.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2025 Adobe. All rights reserved. - * This file is licensed to you under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS - * OF ANY KIND, either express or implied. See the License for the specific language - * governing permissions and limitations under the License. - */ - -/* eslint-env mocha */ - -import { expect } from 'chai'; -import { - OPPORTUNITY_DEPENDENCY_MAP, - getDependenciesForOpportunity, -} from '../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'; - -describe('Opportunity Dependency Map', () => { - describe('OPPORTUNITY_DEPENDENCY_MAP', () => { - it('should contain all expected opportunity types', () => { - expect(OPPORTUNITY_DEPENDENCY_MAP).to.be.an('object'); - expect(OPPORTUNITY_DEPENDENCY_MAP).to.have.property('cwv'); - expect(OPPORTUNITY_DEPENDENCY_MAP).to.have.property('high-organic-low-ctr'); - expect(OPPORTUNITY_DEPENDENCY_MAP).to.have.property('broken-internal-links'); - expect(OPPORTUNITY_DEPENDENCY_MAP).to.have.property('meta-tags'); - expect(OPPORTUNITY_DEPENDENCY_MAP).to.have.property('broken-backlinks'); - }); - - it('should map opportunities to dependencies correctly', () => { - expect(OPPORTUNITY_DEPENDENCY_MAP.cwv).to.deep.equal(['RUM']); - expect(OPPORTUNITY_DEPENDENCY_MAP['high-organic-low-ctr']).to.deep.equal(['RUM']); - expect(OPPORTUNITY_DEPENDENCY_MAP['broken-internal-links']).to.deep.equal(['RUM', 'AHREFSImport']); - expect(OPPORTUNITY_DEPENDENCY_MAP['meta-tags']).to.deep.equal(['AHREFSImport', 'scraping']); - expect(OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']).to.deep.equal(['AHREFSImport', 'scraping']); - expect(OPPORTUNITY_DEPENDENCY_MAP['alt-text']).to.deep.equal(['AHREFSImport', 'scraping']); - expect(OPPORTUNITY_DEPENDENCY_MAP['form-accessibility']).to.deep.equal(['RUM', 'scraping']); - expect(OPPORTUNITY_DEPENDENCY_MAP['forms-opportunities']).to.deep.equal(['RUM', 'scraping']); - }); - }); - - describe('getDependenciesForOpportunity', () => { - it('should return dependencies for known opportunity type', () => { - const dependencies = getDependenciesForOpportunity('cwv'); - expect(dependencies).to.be.an('array'); - expect(dependencies).to.deep.equal(['RUM']); - }); - - it('should return multiple dependencies for broken-internal-links', () => { - const dependencies = getDependenciesForOpportunity('broken-internal-links'); - expect(dependencies).to.be.an('array'); - expect(dependencies).to.have.lengthOf(2); - expect(dependencies).to.include('RUM'); - expect(dependencies).to.include('AHREFSImport'); - }); - - it('should return empty array for opportunity with no dependencies', () => { - const dependencies = getDependenciesForOpportunity('accessibility'); - expect(dependencies).to.be.an('array'); - expect(dependencies).to.have.lengthOf(0); - }); - - it('should return empty array for unknown opportunity type', () => { - const dependencies = getDependenciesForOpportunity('unknown-opportunity'); - expect(dependencies).to.be.an('array'); - expect(dependencies).to.have.lengthOf(0); - }); - - it('should return empty array for null opportunity type', () => { - const dependencies = getDependenciesForOpportunity(null); - expect(dependencies).to.be.an('array'); - expect(dependencies).to.have.lengthOf(0); - }); - }); -}); diff --git a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js index f25f7903..9e32355c 100644 --- a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js +++ b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js @@ -2032,7 +2032,7 @@ describe('Opportunity Status Processor', () => { it('should cover scraping dependency when checked (lines 330-331, 454-457, 595-596, 628-638)', async () => { // Temporarily modify OPPORTUNITY_DEPENDENCY_MAP to include a scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalScraping = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks'] = ['scraping']; @@ -2066,7 +2066,7 @@ describe('Opportunity Status Processor', () => { it('should cover GSC dependency when checked (lines 450-451, 592-593, 646-647)', async () => { // Temporarily modify OPPORTUNITY_DEPENDENCY_MAP to include GSC - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalCwv = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP.cwv; dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP.cwv = ['GSC']; @@ -2113,7 +2113,7 @@ describe('Opportunity Status Processor', () => { const scrapeClientStub = sinon.stub(ScrapeClient, 'createFrom').returns(mockScrapeClient); // Temporarily add scraping dependency to trigger scraping check - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2140,7 +2140,7 @@ describe('Opportunity Status Processor', () => { it('should handle no scrape jobs found (line 149-150)', async () => { // Temporarily add scraping dependency to trigger scraping check - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const mockScrapeClient = { @@ -2193,7 +2193,7 @@ describe('Opportunity Status Processor', () => { const { ScrapeClient } = scrapeModule; // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2246,7 +2246,7 @@ describe('Opportunity Status Processor', () => { const { ScrapeClient } = scrapeModule; // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2299,7 +2299,7 @@ describe('Opportunity Status Processor', () => { const { ScrapeClient } = scrapeModule; // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2366,7 +2366,7 @@ describe('Opportunity Status Processor', () => { const { ScrapeClient } = scrapeModule; // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2427,7 +2427,7 @@ describe('Opportunity Status Processor', () => { const { ScrapeClient } = scrapeModule; // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2558,7 +2558,7 @@ describe('Opportunity Status Processor', () => { const scrapeClientStub = sinon.stub(ScrapeClient, 'createFrom').returns(mockScrapeClient); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -2630,7 +2630,7 @@ describe('Opportunity Status Processor', () => { }); it('should check GSC listSites when GSC is needed (lines 84-86)', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalCwv = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP.cwv; const GoogleClientModule = await import('@adobe/spacecat-shared-google-client'); @@ -2748,7 +2748,7 @@ describe('Opportunity Status Processor', () => { it('should detect bot protection from database and send Slack alert', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; // Make broken-backlinks require scraping @@ -2858,7 +2858,7 @@ describe('Opportunity Status Processor', () => { it('should use dev IPs when AWS_REGION is not us-east', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; // Make broken-backlinks require scraping @@ -2955,7 +2955,7 @@ describe('Opportunity Status Processor', () => { it('should not send bot protection alert when no bot protection logs found', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3024,7 +3024,7 @@ describe('Opportunity Status Processor', () => { it('should handle partial bot protection blocking', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -3124,7 +3124,7 @@ describe('Opportunity Status Processor', () => { it('should not send alert when no bot protection detected', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3211,7 +3211,7 @@ describe('Opportunity Status Processor', () => { }); it('should handle scrapes without bot protection metadata', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3271,7 +3271,7 @@ describe('Opportunity Status Processor', () => { }); it('should not check bot protection when slackContext is missing', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3327,7 +3327,7 @@ describe('Opportunity Status Processor', () => { }); it('should not send alert when all S3 files exist (no missing files)', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3423,7 +3423,7 @@ describe('Opportunity Status Processor', () => { }); it('should use fallback stats when no scrape job ID is available', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; const scrapeModule = await import('@adobe/spacecat-shared-scrape-client'); @@ -3516,7 +3516,7 @@ describe('Opportunity Status Processor', () => { }); it('should handle bot protection without job ID (fallback stats)', async () => { - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks'] = ['scraping']; @@ -3595,7 +3595,7 @@ describe('Opportunity Status Processor', () => { it('should detect bot protection when abortInfo is present', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -3668,7 +3668,7 @@ describe('Opportunity Status Processor', () => { it('should not detect bot protection when abortInfo is null', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -3726,7 +3726,7 @@ describe('Opportunity Status Processor', () => { it('should not detect bot protection when job is complete with no abortInfo', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -3785,7 +3785,7 @@ describe('Opportunity Status Processor', () => { it('should handle errors gracefully when checking bot protection', async function () { this.timeout(5000); - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalBrokenBacklinks = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['broken-backlinks']; try { @@ -3863,7 +3863,7 @@ describe('Opportunity Status Processor', () => { }); // Temporarily add scraping dependency - const dependencyMapModule = await import('../../../src/tasks/opportunity-status-processor/opportunity-dependency-map.js'); + const dependencyMapModule = await import('@adobe/spacecat-shared-utils'); const originalAltText = dependencyMapModule.OPPORTUNITY_DEPENDENCY_MAP['alt-text']; try { @@ -4011,6 +4011,8 @@ describe('Opportunity Status Processor', () => { const handler = await esmock('../../../src/tasks/opportunity-status-processor/handler.js', { '@adobe/spacecat-shared-utils': { resolveCanonicalUrl: sinon.stub().resolves('https://example.com'), + getOpportunitiesForAudit: mockGetOpportunitiesForAudit, + AUDIT_OPPORTUNITY_MAP: {}, }, '../../../src/utils/bot-detection.js': { checkAndAlertBotProtection: sinon.stub().resolves(null), @@ -4018,10 +4020,6 @@ describe('Opportunity Status Processor', () => { '../../../src/utils/cloudwatch-utils.js': { getAuditStatus: sinon.stub().resolves({ executed: true, failureReason: null }), }, - '../../../src/tasks/opportunity-status-processor/audit-opportunity-map.js': { - getOpportunitiesForAudit: mockGetOpportunitiesForAudit, - AUDIT_OPPORTUNITY_MAP: {}, - }, }); // Create a scenario where an opportunity type exists but no audits in auditTypes From ab2e3c0027c2b51b0e01360425ea1396ce872470 Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Fri, 27 Mar 2026 10:47:49 -0500 Subject: [PATCH 5/8] review comments --- src/tasks/opportunity-status-processor/handler.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index 7153072c..4dad88fb 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -247,8 +247,8 @@ async function checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, pendingAuditTypes.push(auditType); } else { const auditedAt = new Date(audit.getAuditedAt()).getTime(); - if (onboardStartTime && auditedAt < onboardStartTime) { - // Record exists but predates this onboard session — treat as pending + if (Number.isNaN(auditedAt) || auditedAt < onboardStartTime) { + // Unparseable timestamp or audit predates this onboard session — treat as pending pendingAuditTypes.push(auditType); } else { completedAuditTypes.push(auditType); @@ -258,6 +258,8 @@ async function checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, } catch (error) { log.warn(`Could not check audit completion from DB for site ${siteId}: ${error.message}`); // Conservative fallback: mark all as pending so disclaimer is always shown on error + // Reset first to avoid duplicates if some types were already pushed before the error + pendingAuditTypes.length = 0; pendingAuditTypes.push(...auditTypes.filter((t) => !completedAuditTypes.includes(t))); } return { pendingAuditTypes, completedAuditTypes }; @@ -722,7 +724,10 @@ export async function runOpportunityStatusProcessor(message, context) { (t) => getOpportunitiesForAudit(t).length > 0, ); if (relevantPendingTypes.length > 0) { - const pendingList = relevantPendingTypes.map(getOpportunityTitle).join(', '); + const pendingOpportunityNames = relevantPendingTypes + .flatMap((t) => getOpportunitiesForAudit(t)) + .map(getOpportunityTitle); + const pendingList = [...new Set(pendingOpportunityNames)].join(', '); await say( env, log, From edfaeb8620717a6e4bf6d3fde6699b2282156da2 Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Sun, 29 Mar 2026 20:21:14 -0500 Subject: [PATCH 6/8] address review comments --- package-lock.json | 12 ++-- package.json | 4 +- .../opportunity-status-processor/handler.js | 61 ++++--------------- 3 files changed, 19 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 77ec97cc..f0a67a24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/c18a23bd0566c296ad75701b84ffbef5/raw/dd0062c172263f60f98c8dee6f7a608c7dffabf0/adobe-spacecat-shared-data-access-3.32.1.tgz", + "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.8", "@adobe/spacecat-shared-gpt-client": "1.6.20", @@ -23,7 +23,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.10", "@adobe/spacecat-shared-scrape-client": "2.5.4", "@adobe/spacecat-shared-slack-client": "1.6.4", - "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/3e5c80c528d7b3d17d1e3d08d708573b/raw/33821e8f69e83f13d40f39f192d383e32276f58f/adobe-spacecat-shared-utils-1.106.1.tgz", + "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", "@adobe/spacecat-shared-vault-secrets": "1.3.1", "@aws-sdk/client-cloudwatch-logs": "3.1019.0", "@aws-sdk/client-lambda": "3.1019.0", @@ -590,8 +590,8 @@ }, "node_modules/@adobe/spacecat-shared-data-access": { "version": "3.32.1", - "resolved": "https://gist.github.com/tkotthakota-adobe/c18a23bd0566c296ad75701b84ffbef5/raw/dd0062c172263f60f98c8dee6f7a608c7dffabf0/adobe-spacecat-shared-data-access-3.32.1.tgz", - "integrity": "sha512-nVw+Bjbu357+/25CHh+wSVLamWglDqqEBeX2MIiM285WqNxwvJT5DJ+V1m2499g0raU4UlhxlKnqWjREIRGZ/w==", + "resolved": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", + "integrity": "sha512-wp1/AQVF/beUwJXzgZRKl1zjuf4ehS5S3o5cCqk/sO7ZcJbXDsLnkXlM1kKi/4kA4inHRJ7x/vjuuHeOTi0Jvw==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "^4.2.3", @@ -4280,8 +4280,8 @@ }, "node_modules/@adobe/spacecat-shared-utils": { "version": "1.106.1", - "resolved": "https://gist.github.com/tkotthakota-adobe/3e5c80c528d7b3d17d1e3d08d708573b/raw/33821e8f69e83f13d40f39f192d383e32276f58f/adobe-spacecat-shared-utils-1.106.1.tgz", - "integrity": "sha512-eQSaIFMBLVC5K63ZnJPldsDwq+1drDO+5plIgnEpMiztW8UfsccMGgpOs/qsT/4wsSgTEKcuWuLPVnpIVeTcQg==", + "resolved": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", + "integrity": "sha512-WVDvjZ6rFvN1EhOXAAAWhHa7irPa8VVDcVUo2lBbSZX2t2vpxYmG5ik0KKHCW4E2mCiMvKIH44TmDza/wkfQMA==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "4.2.3", diff --git a/package.json b/package.json index 22ea1739..dc99363b 100755 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/c18a23bd0566c296ad75701b84ffbef5/raw/dd0062c172263f60f98c8dee6f7a608c7dffabf0/adobe-spacecat-shared-data-access-3.32.1.tgz", + "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.8", "@adobe/spacecat-shared-gpt-client": "1.6.20", @@ -85,7 +85,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.10", "@adobe/spacecat-shared-scrape-client": "2.5.4", "@adobe/spacecat-shared-slack-client": "1.6.4", - "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/3e5c80c528d7b3d17d1e3d08d708573b/raw/33821e8f69e83f13d40f39f192d383e32276f58f/adobe-spacecat-shared-utils-1.106.1.tgz", + "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", "@aws-sdk/client-lambda": "3.1019.0", "@aws-sdk/client-sqs": "3.1019.0", "@aws-sdk/client-s3": "3.1019.0", diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index 4dad88fb..195eb32b 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -20,6 +20,7 @@ import { getOpportunityTitle, OPPORTUNITY_DEPENDENCY_MAP, getOpportunitiesForAudit, + computeAuditCompletion, } from '@adobe/spacecat-shared-utils'; import { getAuditStatus } from '../../utils/cloudwatch-utils.js'; import { checkAndAlertBotProtection } from '../../utils/bot-detection.js'; @@ -217,54 +218,6 @@ async function isScrapingAvailable(baseUrl, context, onboardStartTime) { } } -/** - * Checks which audit types have completed since onboardStartTime by querying the database. - * An audit is considered completed if a record exists with auditedAt >= onboardStartTime. - * Falls back conservatively (all pending) if the DB query fails. - * - * @param {string} siteId - The site ID - * @param {Array} auditTypes - Audit types expected for this onboard session - * @param {number} onboardStartTime - Onboarding start timestamp in ms - * @param {object} dataAccess - Data access object - * @param {object} log - Logger - * @returns {Promise<{pendingAuditTypes: Array, completedAuditTypes: Array}>} - */ -async function checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, dataAccess, log) { - const pendingAuditTypes = []; - const completedAuditTypes = []; - try { - const { Audit } = dataAccess; - const latestAudits = await Audit.allLatestForSite(siteId); - const auditsByType = {}; - if (latestAudits) { - for (const audit of latestAudits) { - auditsByType[audit.getAuditType()] = audit; - } - } - for (const auditType of auditTypes) { - const audit = auditsByType[auditType]; - if (!audit) { - pendingAuditTypes.push(auditType); - } else { - const auditedAt = new Date(audit.getAuditedAt()).getTime(); - if (Number.isNaN(auditedAt) || auditedAt < onboardStartTime) { - // Unparseable timestamp or audit predates this onboard session — treat as pending - pendingAuditTypes.push(auditType); - } else { - completedAuditTypes.push(auditType); - } - } - } - } catch (error) { - log.warn(`Could not check audit completion from DB for site ${siteId}: ${error.message}`); - // Conservative fallback: mark all as pending so disclaimer is always shown on error - // Reset first to avoid duplicates if some types were already pushed before the error - pendingAuditTypes.length = 0; - pendingAuditTypes.push(...auditTypes.filter((t) => !completedAuditTypes.includes(t))); - } - return { pendingAuditTypes, completedAuditTypes }; -} - /** * Analyzes missing opportunities and determines the root cause * @param {Array} missingOpportunities - Array of missing opportunity types @@ -582,8 +535,16 @@ export async function runOpportunityStatusProcessor(message, context) { // Only meaningful when we have an onboardStartTime anchor to compare against. let pendingAuditTypes = []; if (auditTypes && auditTypes.length > 0 && onboardStartTime) { - // eslint-disable-next-line max-len - ({ pendingAuditTypes } = await checkAuditCompletionFromDB(siteId, auditTypes, onboardStartTime, dataAccess, log)); + try { + const { Audit } = dataAccess; + const latestAudits = await Audit.allLatestForSite(siteId); + const completion = computeAuditCompletion(auditTypes, onboardStartTime, latestAudits); + pendingAuditTypes = completion.pendingAuditTypes; + } catch (auditErr) { + log.warn(`Could not check audit completion from DB for site ${siteId}: ${auditErr.message}`); + // Conservative fallback: mark all as pending so disclaimer is always shown on error + pendingAuditTypes = [...auditTypes]; + } } // Process opportunities by type to avoid duplicates From 4a16f672afad47102dd33759b00171d54c341fba Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Sun, 29 Mar 2026 21:37:41 -0500 Subject: [PATCH 7/8] update lib --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0a67a24..3f05d3bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", + "@adobe/spacecat-shared-data-access": "3.33.0", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.8", "@adobe/spacecat-shared-gpt-client": "1.6.20", @@ -23,7 +23,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.10", "@adobe/spacecat-shared-scrape-client": "2.5.4", "@adobe/spacecat-shared-slack-client": "1.6.4", - "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", + "@adobe/spacecat-shared-utils": "1.107.0", "@adobe/spacecat-shared-vault-secrets": "1.3.1", "@aws-sdk/client-cloudwatch-logs": "3.1019.0", "@aws-sdk/client-lambda": "3.1019.0", @@ -589,9 +589,9 @@ } }, "node_modules/@adobe/spacecat-shared-data-access": { - "version": "3.32.1", - "resolved": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", - "integrity": "sha512-wp1/AQVF/beUwJXzgZRKl1zjuf4ehS5S3o5cCqk/sO7ZcJbXDsLnkXlM1kKi/4kA4inHRJ7x/vjuuHeOTi0Jvw==", + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-data-access/-/spacecat-shared-data-access-3.33.0.tgz", + "integrity": "sha512-Av1sqLjkcNItGeewO+arAHXLSbyN12zZ/+i6nltlXZxhQt0W2dqa7uxlpIY66cVl/dNykJ26InwUZA5sAJRzXw==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "^4.2.3", @@ -4279,9 +4279,9 @@ } }, "node_modules/@adobe/spacecat-shared-utils": { - "version": "1.106.1", - "resolved": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", - "integrity": "sha512-WVDvjZ6rFvN1EhOXAAAWhHa7irPa8VVDcVUo2lBbSZX2t2vpxYmG5ik0KKHCW4E2mCiMvKIH44TmDza/wkfQMA==", + "version": "1.107.0", + "resolved": "https://registry.npmjs.org/@adobe/spacecat-shared-utils/-/spacecat-shared-utils-1.107.0.tgz", + "integrity": "sha512-if80uefULP31rfpTg3C2M1pofqk8XosmRpQ1QOs6dHpAeEYudqIfMghg/oc7wDg7z8/AIDFKXK+N0AVx4lyTnQ==", "license": "Apache-2.0", "dependencies": { "@adobe/fetch": "4.2.3", diff --git a/package.json b/package.json index dc99363b..32965033 100755 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@adobe/helix-status": "10.1.5", "@adobe/helix-universal": "5.4.0", "@adobe/helix-universal-logger": "3.0.28", - "@adobe/spacecat-shared-data-access": "https://gist.github.com/tkotthakota-adobe/ab119c43b3a874bcda91240f5a458179/raw/eddd18886cbf1d4285418e1f60d987ebd29dc333/adobe-spacecat-shared-data-access-3.32.1.tgz", + "@adobe/spacecat-shared-data-access": "3.33.0", "@adobe/spacecat-shared-data-access-v2": "npm:@adobe/spacecat-shared-data-access@2.109.0", "@adobe/spacecat-shared-google-client": "1.5.8", "@adobe/spacecat-shared-gpt-client": "1.6.20", @@ -85,7 +85,7 @@ "@adobe/spacecat-shared-rum-api-client": "2.40.10", "@adobe/spacecat-shared-scrape-client": "2.5.4", "@adobe/spacecat-shared-slack-client": "1.6.4", - "@adobe/spacecat-shared-utils": "https://gist.github.com/tkotthakota-adobe/24594fa3013e22c6183dacebe2293b42/raw/114b0fba15511512ecfa441dfd91a4a163d029ab/adobe-spacecat-shared-utils-1.106.1.tgz", + "@adobe/spacecat-shared-utils": "1.107.0", "@aws-sdk/client-lambda": "3.1019.0", "@aws-sdk/client-sqs": "3.1019.0", "@aws-sdk/client-s3": "3.1019.0", From 1a7e02dbef6af3109ec062c7493ca115c5beb162 Mon Sep 17 00:00:00 2001 From: Tej Kotthakota Date: Mon, 30 Mar 2026 09:45:55 -0500 Subject: [PATCH 8/8] addressed review comments --- .../opportunity-status-processor/handler.js | 2 +- .../opportunity-status-processor.test.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/tasks/opportunity-status-processor/handler.js b/src/tasks/opportunity-status-processor/handler.js index 195eb32b..d852d29e 100644 --- a/src/tasks/opportunity-status-processor/handler.js +++ b/src/tasks/opportunity-status-processor/handler.js @@ -698,7 +698,7 @@ export async function runOpportunityStatusProcessor(message, context) { + 'The statuses above reflect data available at this moment and may be incomplete. ' + `Run \`onboard status ${siteUrl}\` to re-check once all audits have completed.`, ); - } else if (isRecheck) { + } else if (isRecheck && onboardStartTime) { await say( env, log, diff --git a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js index 9e32355c..692625b1 100644 --- a/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js +++ b/test/tasks/opportunity-status-processor/opportunity-status-processor.test.js @@ -75,6 +75,9 @@ describe('Opportunity Status Processor', () => { SiteTopPage: { allBySiteIdAndSourceAndGeo: sandbox.stub().resolves([]), }, + Audit: { + allLatestForSite: sandbox.stub().resolves([]), + }, }) .withOverrides({ s3Client: mockS3Client, @@ -4397,5 +4400,21 @@ describe('Opportunity Status Processor', () => { expect(disclaimer).to.include('Core Web Vitals'); expect(disclaimer).to.not.include('Scrape Top Pages'); }); + + it('does not send "all complete" when isRecheck=true but onboardStartTime is absent (legacy site)', async () => { + // onboardStartTime is undefined — audit completion check is skipped entirely. + // pendingAuditTypes stays [] but we must NOT send "All audits have completed" + // because no check was performed. + const testMessage = makeDisclaimerMessage(undefined, ['cwv'], true); + const testContext = makeDisclaimerContext([]); + + await disclaimerHandler.runOpportunityStatusProcessor(testMessage, testContext); + + // DB should not have been queried (no onboardStartTime to compare against) + expect(testContext.dataAccess.Audit.allLatestForSite).to.not.have.been.called; + const disclaimerCalls = sayStub.args.map((a) => a[3]).filter(Boolean); + expect(disclaimerCalls.some((m) => m.includes('All audits have completed'))).to.be.false; + expect(disclaimerCalls.some((m) => m.includes('may still be in progress'))).to.be.false; + }); }); });