@rankcli/agent-runtime 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -0
- package/dist/analyzer-2CSWIQGD.mjs +6 -0
- package/dist/chunk-YNZYHEYM.mjs +774 -0
- package/dist/index.d.mts +4012 -0
- package/dist/index.d.ts +4012 -0
- package/dist/index.js +29672 -0
- package/dist/index.mjs +28602 -0
- package/package.json +53 -0
- package/scripts/build-deno.ts +134 -0
- package/src/audit/ai/analyzer.ts +347 -0
- package/src/audit/ai/index.ts +29 -0
- package/src/audit/ai/prompts/content-analysis.ts +271 -0
- package/src/audit/ai/types.ts +179 -0
- package/src/audit/checks/additional-checks.ts +439 -0
- package/src/audit/checks/ai-citation-worthiness.ts +399 -0
- package/src/audit/checks/ai-content-structure.ts +325 -0
- package/src/audit/checks/ai-readiness.ts +339 -0
- package/src/audit/checks/anchor-text.ts +179 -0
- package/src/audit/checks/answer-conciseness.ts +322 -0
- package/src/audit/checks/asset-minification.ts +270 -0
- package/src/audit/checks/bing-optimization.ts +206 -0
- package/src/audit/checks/brand-mention-optimization.ts +349 -0
- package/src/audit/checks/caching-headers.ts +305 -0
- package/src/audit/checks/canonical-advanced.ts +150 -0
- package/src/audit/checks/canonical-domain.ts +196 -0
- package/src/audit/checks/citation-quality.ts +358 -0
- package/src/audit/checks/client-rendering.ts +542 -0
- package/src/audit/checks/color-contrast.ts +342 -0
- package/src/audit/checks/content-freshness.ts +170 -0
- package/src/audit/checks/content-science.ts +589 -0
- package/src/audit/checks/conversion-elements.ts +526 -0
- package/src/audit/checks/crawlability.ts +220 -0
- package/src/audit/checks/directory-listing.ts +172 -0
- package/src/audit/checks/dom-analysis.ts +191 -0
- package/src/audit/checks/dom-size.ts +246 -0
- package/src/audit/checks/duplicate-content.ts +194 -0
- package/src/audit/checks/eeat-signals.ts +990 -0
- package/src/audit/checks/entity-seo.ts +396 -0
- package/src/audit/checks/featured-snippet.ts +473 -0
- package/src/audit/checks/freshness-signals.ts +443 -0
- package/src/audit/checks/funnel-intent.ts +463 -0
- package/src/audit/checks/hreflang.ts +174 -0
- package/src/audit/checks/html-compliance.ts +302 -0
- package/src/audit/checks/image-dimensions.ts +167 -0
- package/src/audit/checks/images.ts +160 -0
- package/src/audit/checks/indexnow.ts +275 -0
- package/src/audit/checks/interactive-tools.ts +475 -0
- package/src/audit/checks/internal-link-graph.ts +436 -0
- package/src/audit/checks/keyword-analysis.ts +239 -0
- package/src/audit/checks/keyword-cannibalization.ts +385 -0
- package/src/audit/checks/keyword-placement.ts +471 -0
- package/src/audit/checks/links.ts +203 -0
- package/src/audit/checks/llms-txt.ts +224 -0
- package/src/audit/checks/local-seo.ts +296 -0
- package/src/audit/checks/mobile.ts +167 -0
- package/src/audit/checks/modern-images.ts +226 -0
- package/src/audit/checks/navboost-signals.ts +395 -0
- package/src/audit/checks/on-page.ts +209 -0
- package/src/audit/checks/page-resources.ts +285 -0
- package/src/audit/checks/pagination.ts +180 -0
- package/src/audit/checks/performance.ts +153 -0
- package/src/audit/checks/platform-presence.ts +580 -0
- package/src/audit/checks/redirect-analysis.ts +153 -0
- package/src/audit/checks/redirect-chain.ts +389 -0
- package/src/audit/checks/resource-hints.ts +420 -0
- package/src/audit/checks/responsive-css.ts +247 -0
- package/src/audit/checks/responsive-images.ts +396 -0
- package/src/audit/checks/review-ecosystem.ts +415 -0
- package/src/audit/checks/robots-validation.ts +373 -0
- package/src/audit/checks/security-headers.ts +172 -0
- package/src/audit/checks/security.ts +144 -0
- package/src/audit/checks/serp-preview.ts +251 -0
- package/src/audit/checks/site-maturity.ts +444 -0
- package/src/audit/checks/social-meta.test.ts +275 -0
- package/src/audit/checks/social-meta.ts +134 -0
- package/src/audit/checks/soft-404.ts +151 -0
- package/src/audit/checks/structured-data.ts +238 -0
- package/src/audit/checks/tech-detection.ts +496 -0
- package/src/audit/checks/topical-clusters.ts +435 -0
- package/src/audit/checks/tracker-bloat.ts +462 -0
- package/src/audit/checks/tracking-verification.test.ts +371 -0
- package/src/audit/checks/tracking-verification.ts +636 -0
- package/src/audit/checks/url-safety.ts +682 -0
- package/src/audit/deno-entry.ts +66 -0
- package/src/audit/discovery/index.ts +15 -0
- package/src/audit/discovery/link-crawler.ts +232 -0
- package/src/audit/discovery/repo-routes.ts +347 -0
- package/src/audit/engine.ts +620 -0
- package/src/audit/fixes/index.ts +209 -0
- package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
- package/src/audit/fixes/social-meta-fixes.ts +463 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/runner.test.ts +299 -0
- package/src/audit/runner.ts +130 -0
- package/src/audit/types.ts +1953 -0
- package/src/content/featured-snippet.ts +367 -0
- package/src/content/generator.test.ts +534 -0
- package/src/content/generator.ts +501 -0
- package/src/content/headline.ts +317 -0
- package/src/content/index.ts +62 -0
- package/src/content/intent.ts +258 -0
- package/src/content/keyword-density.ts +349 -0
- package/src/content/readability.ts +262 -0
- package/src/executor.ts +336 -0
- package/src/fixer.ts +416 -0
- package/src/frameworks/detector.test.ts +248 -0
- package/src/frameworks/detector.ts +371 -0
- package/src/frameworks/index.ts +68 -0
- package/src/frameworks/recipes/angular.yaml +171 -0
- package/src/frameworks/recipes/astro.yaml +206 -0
- package/src/frameworks/recipes/django.yaml +180 -0
- package/src/frameworks/recipes/laravel.yaml +137 -0
- package/src/frameworks/recipes/nextjs.yaml +268 -0
- package/src/frameworks/recipes/nuxt.yaml +175 -0
- package/src/frameworks/recipes/rails.yaml +188 -0
- package/src/frameworks/recipes/react.yaml +202 -0
- package/src/frameworks/recipes/sveltekit.yaml +154 -0
- package/src/frameworks/recipes/vue.yaml +137 -0
- package/src/frameworks/recipes/wordpress.yaml +209 -0
- package/src/frameworks/suggestion-engine.ts +320 -0
- package/src/geo/geo-content.test.ts +305 -0
- package/src/geo/geo-content.ts +266 -0
- package/src/geo/geo-history.test.ts +473 -0
- package/src/geo/geo-history.ts +433 -0
- package/src/geo/geo-tracker.test.ts +359 -0
- package/src/geo/geo-tracker.ts +411 -0
- package/src/geo/index.ts +10 -0
- package/src/git/commit-helper.test.ts +261 -0
- package/src/git/commit-helper.ts +329 -0
- package/src/git/index.ts +12 -0
- package/src/git/pr-helper.test.ts +284 -0
- package/src/git/pr-helper.ts +307 -0
- package/src/index.ts +66 -0
- package/src/keywords/ai-keyword-engine.ts +1062 -0
- package/src/keywords/ai-summarizer.ts +387 -0
- package/src/keywords/ci-mode.ts +555 -0
- package/src/keywords/engine.ts +359 -0
- package/src/keywords/index.ts +151 -0
- package/src/keywords/llm-judge.ts +357 -0
- package/src/keywords/nlp-analysis.ts +706 -0
- package/src/keywords/prioritizer.ts +295 -0
- package/src/keywords/site-crawler.ts +342 -0
- package/src/keywords/sources/autocomplete.ts +139 -0
- package/src/keywords/sources/competitive-search.ts +450 -0
- package/src/keywords/sources/competitor-analysis.ts +374 -0
- package/src/keywords/sources/dataforseo.ts +206 -0
- package/src/keywords/sources/free-sources.ts +294 -0
- package/src/keywords/sources/gsc.ts +123 -0
- package/src/keywords/topic-grouping.ts +327 -0
- package/src/keywords/types.ts +144 -0
- package/src/keywords/wizard.ts +457 -0
- package/src/loader.ts +40 -0
- package/src/reports/index.ts +7 -0
- package/src/reports/report-generator.test.ts +293 -0
- package/src/reports/report-generator.ts +713 -0
- package/src/scheduler/alerts.test.ts +458 -0
- package/src/scheduler/alerts.ts +328 -0
- package/src/scheduler/index.ts +8 -0
- package/src/scheduler/scheduled-audit.test.ts +377 -0
- package/src/scheduler/scheduled-audit.ts +149 -0
- package/src/test/integration-test.ts +325 -0
- package/src/tools/analyzer.ts +373 -0
- package/src/tools/crawl.ts +293 -0
- package/src/tools/files.ts +301 -0
- package/src/tools/h1-fixer.ts +249 -0
- package/src/tools/index.ts +67 -0
- package/src/tracking/github-action.ts +326 -0
- package/src/tracking/google-analytics.ts +265 -0
- package/src/tracking/index.ts +45 -0
- package/src/tracking/report-generator.ts +386 -0
- package/src/tracking/search-console.ts +335 -0
- package/src/types.ts +134 -0
- package/src/utils/http.ts +302 -0
- package/src/wasm-adapter.ts +297 -0
- package/src/wasm-entry.ts +14 -0
- package/tsconfig.json +17 -0
- package/tsup.wasm.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alerts Module
|
|
3
|
+
*
|
|
4
|
+
* Sends notifications via Slack, Discord, and email webhooks when SEO events occur.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type AlertType = 'error' | 'score_drop' | 'weekly_report' | 'pr_created';
|
|
8
|
+
export type AlertChannel = 'slack' | 'discord' | 'email';
|
|
9
|
+
export type AlertSeverity = 'info' | 'success' | 'warning' | 'error';
|
|
10
|
+
|
|
11
|
+
export interface AlertConfig {
|
|
12
|
+
type: AlertChannel;
|
|
13
|
+
webhookUrl: string;
|
|
14
|
+
on: AlertType[];
|
|
15
|
+
scoreDropThreshold?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AuditAlertPayload {
|
|
19
|
+
projectId: string;
|
|
20
|
+
projectName: string;
|
|
21
|
+
url: string;
|
|
22
|
+
score: number;
|
|
23
|
+
previousScore?: number;
|
|
24
|
+
issuesFound: number;
|
|
25
|
+
errorCount?: number;
|
|
26
|
+
prUrl?: string;
|
|
27
|
+
prNumber?: number;
|
|
28
|
+
timestamp: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AlertMessage {
|
|
32
|
+
title: string;
|
|
33
|
+
body: string;
|
|
34
|
+
severity: AlertSeverity;
|
|
35
|
+
url?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface AlertResult {
|
|
39
|
+
success: boolean;
|
|
40
|
+
skipped?: boolean;
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const SEVERITY_COLORS: Record<AlertSeverity, { slack: string; discord: number }> = {
|
|
45
|
+
info: { slack: '#2196F3', discord: 0x2196f3 },
|
|
46
|
+
success: { slack: '#4CAF50', discord: 0x4caf50 },
|
|
47
|
+
warning: { slack: '#FF9800', discord: 0xff9800 },
|
|
48
|
+
error: { slack: '#F44336', discord: 0xf44336 },
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Formats an alert message based on type and payload
|
|
53
|
+
*/
|
|
54
|
+
export function formatAlertMessage(type: AlertType, payload: AuditAlertPayload): AlertMessage {
|
|
55
|
+
const { projectName, url, score, previousScore, issuesFound, errorCount, prUrl, prNumber } = payload;
|
|
56
|
+
|
|
57
|
+
switch (type) {
|
|
58
|
+
case 'score_drop': {
|
|
59
|
+
const change = previousScore !== undefined ? score - previousScore : 0;
|
|
60
|
+
return {
|
|
61
|
+
title: `⚠️ Score Drop Alert: ${projectName}`,
|
|
62
|
+
body: `The SEO score for ${url} dropped from ${previousScore} to ${score} (${change} points).\n\n` +
|
|
63
|
+
`Issues found: ${issuesFound}`,
|
|
64
|
+
severity: 'warning',
|
|
65
|
+
url,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case 'error': {
|
|
70
|
+
return {
|
|
71
|
+
title: `🚨 Critical Issues Detected: ${projectName}`,
|
|
72
|
+
body: `The SEO audit for ${url} found ${errorCount || issuesFound} errors that need immediate attention.\n\n` +
|
|
73
|
+
`Current score: ${score}/100`,
|
|
74
|
+
severity: 'error',
|
|
75
|
+
url,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
case 'weekly_report': {
|
|
80
|
+
const change = previousScore !== undefined ? score - previousScore : 0;
|
|
81
|
+
const changeText = change > 0 ? `+${change}` : change.toString();
|
|
82
|
+
return {
|
|
83
|
+
title: `📊 Weekly SEO Report: ${projectName}`,
|
|
84
|
+
body: `Your weekly SEO summary for ${url}:\n\n` +
|
|
85
|
+
`• Score: ${score}/100 (${previousScore !== undefined ? changeText + ' from last week' : 'baseline'})\n` +
|
|
86
|
+
`• Issues: ${issuesFound}`,
|
|
87
|
+
severity: 'info',
|
|
88
|
+
url,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'pr_created': {
|
|
93
|
+
return {
|
|
94
|
+
title: `✅ PR Created: ${projectName}`,
|
|
95
|
+
body: `A new pull request (#${prNumber}) has been created to fix SEO issues for ${url}.\n\n` +
|
|
96
|
+
`View PR: ${prUrl?.replace('https://', '')}`,
|
|
97
|
+
severity: 'success',
|
|
98
|
+
url: prUrl,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
default:
|
|
103
|
+
return {
|
|
104
|
+
title: `SEO Alert: ${projectName}`,
|
|
105
|
+
body: `Score: ${score}/100, Issues: ${issuesFound}`,
|
|
106
|
+
severity: 'info',
|
|
107
|
+
url,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Determines if an alert should be sent based on config and payload
|
|
114
|
+
*/
|
|
115
|
+
export function shouldSendAlert(
|
|
116
|
+
config: AlertConfig,
|
|
117
|
+
type: AlertType,
|
|
118
|
+
payload?: AuditAlertPayload
|
|
119
|
+
): boolean {
|
|
120
|
+
// Check if type is in config.on
|
|
121
|
+
if (!config.on.includes(type)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Special handling for score_drop with threshold
|
|
126
|
+
if (type === 'score_drop' && config.scoreDropThreshold && payload) {
|
|
127
|
+
const drop = (payload.previousScore ?? 0) - payload.score;
|
|
128
|
+
return drop >= config.scoreDropThreshold;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Formats a Slack message with blocks
|
|
136
|
+
*/
|
|
137
|
+
function formatSlackMessage(message: AlertMessage): object {
|
|
138
|
+
const color = SEVERITY_COLORS[message.severity].slack;
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
attachments: [
|
|
142
|
+
{
|
|
143
|
+
color,
|
|
144
|
+
blocks: [
|
|
145
|
+
{
|
|
146
|
+
type: 'header',
|
|
147
|
+
text: {
|
|
148
|
+
type: 'plain_text',
|
|
149
|
+
text: message.title,
|
|
150
|
+
emoji: true,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: 'section',
|
|
155
|
+
text: {
|
|
156
|
+
type: 'mrkdwn',
|
|
157
|
+
text: message.body,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
...(message.url
|
|
161
|
+
? [
|
|
162
|
+
{
|
|
163
|
+
type: 'section',
|
|
164
|
+
text: {
|
|
165
|
+
type: 'mrkdwn',
|
|
166
|
+
text: `<${message.url}|View Details>`,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
]
|
|
170
|
+
: []),
|
|
171
|
+
{
|
|
172
|
+
type: 'context',
|
|
173
|
+
elements: [
|
|
174
|
+
{
|
|
175
|
+
type: 'mrkdwn',
|
|
176
|
+
text: `🤖 Sent by SEO Autopilot`,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
blocks: [
|
|
184
|
+
{
|
|
185
|
+
type: 'header',
|
|
186
|
+
text: {
|
|
187
|
+
type: 'plain_text',
|
|
188
|
+
text: message.title,
|
|
189
|
+
emoji: true,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'section',
|
|
194
|
+
text: {
|
|
195
|
+
type: 'mrkdwn',
|
|
196
|
+
text: message.body,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Formats a Discord message with embeds
|
|
205
|
+
*/
|
|
206
|
+
function formatDiscordMessage(message: AlertMessage): object {
|
|
207
|
+
const color = SEVERITY_COLORS[message.severity].discord;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
embeds: [
|
|
211
|
+
{
|
|
212
|
+
title: message.title,
|
|
213
|
+
description: message.body,
|
|
214
|
+
color,
|
|
215
|
+
url: message.url,
|
|
216
|
+
footer: {
|
|
217
|
+
text: '🤖 Sent by SEO Autopilot',
|
|
218
|
+
},
|
|
219
|
+
timestamp: new Date().toISOString(),
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Sends an alert to Slack webhook
|
|
227
|
+
*/
|
|
228
|
+
export async function sendSlackAlert(
|
|
229
|
+
webhookUrl: string,
|
|
230
|
+
type: AlertType,
|
|
231
|
+
payload: AuditAlertPayload
|
|
232
|
+
): Promise<AlertResult> {
|
|
233
|
+
const message = formatAlertMessage(type, payload);
|
|
234
|
+
const slackMessage = formatSlackMessage(message);
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const response = await fetch(webhookUrl, {
|
|
238
|
+
method: 'POST',
|
|
239
|
+
headers: { 'Content-Type': 'application/json' },
|
|
240
|
+
body: JSON.stringify(slackMessage),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
return {
|
|
245
|
+
success: false,
|
|
246
|
+
error: `Slack webhook failed with status ${response.status}`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return { success: true };
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return {
|
|
253
|
+
success: false,
|
|
254
|
+
error: (error as Error).message,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Sends an alert to Discord webhook
|
|
261
|
+
*/
|
|
262
|
+
export async function sendDiscordAlert(
|
|
263
|
+
webhookUrl: string,
|
|
264
|
+
type: AlertType,
|
|
265
|
+
payload: AuditAlertPayload
|
|
266
|
+
): Promise<AlertResult> {
|
|
267
|
+
const message = formatAlertMessage(type, payload);
|
|
268
|
+
const discordMessage = formatDiscordMessage(message);
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const response = await fetch(webhookUrl, {
|
|
272
|
+
method: 'POST',
|
|
273
|
+
headers: { 'Content-Type': 'application/json' },
|
|
274
|
+
body: JSON.stringify(discordMessage),
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
if (!response.ok) {
|
|
278
|
+
return {
|
|
279
|
+
success: false,
|
|
280
|
+
error: `Discord webhook failed with status ${response.status}`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { success: true };
|
|
285
|
+
} catch (error) {
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
error: (error as Error).message,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Sends an alert based on config
|
|
295
|
+
*/
|
|
296
|
+
export async function sendAlert(
|
|
297
|
+
config: AlertConfig,
|
|
298
|
+
type: AlertType,
|
|
299
|
+
payload: AuditAlertPayload
|
|
300
|
+
): Promise<AlertResult> {
|
|
301
|
+
// Check if we should send this alert
|
|
302
|
+
if (!shouldSendAlert(config, type, payload)) {
|
|
303
|
+
return { success: true, skipped: true };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
switch (config.type) {
|
|
307
|
+
case 'slack':
|
|
308
|
+
return sendSlackAlert(config.webhookUrl, type, payload);
|
|
309
|
+
case 'discord':
|
|
310
|
+
return sendDiscordAlert(config.webhookUrl, type, payload);
|
|
311
|
+
case 'email':
|
|
312
|
+
// Email would require SMTP setup - skip for now
|
|
313
|
+
return { success: true, skipped: true };
|
|
314
|
+
default:
|
|
315
|
+
return { success: false, error: `Unknown alert type: ${config.type}` };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Sends alerts to multiple configs
|
|
321
|
+
*/
|
|
322
|
+
export async function sendAlerts(
|
|
323
|
+
configs: AlertConfig[],
|
|
324
|
+
type: AlertType,
|
|
325
|
+
payload: AuditAlertPayload
|
|
326
|
+
): Promise<AlertResult[]> {
|
|
327
|
+
return Promise.all(configs.map((config) => sendAlert(config, type, payload)));
|
|
328
|
+
}
|