@junando/webhook 0.7.4 → 0.8.0
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/dist/handler.cjs +169 -82
- package/package.json +2 -2
package/dist/handler.cjs
CHANGED
|
@@ -4468,62 +4468,6 @@ var init_normalize_payload = __esm({
|
|
|
4468
4468
|
}
|
|
4469
4469
|
});
|
|
4470
4470
|
|
|
4471
|
-
// ../core/src/application/use-cases/process-incident.use-case.ts
|
|
4472
|
-
var ProcessIncidentUseCase;
|
|
4473
|
-
var init_process_incident_use_case = __esm({
|
|
4474
|
-
"../core/src/application/use-cases/process-incident.use-case.ts"() {
|
|
4475
|
-
init_clustering_service();
|
|
4476
|
-
ProcessIncidentUseCase = class {
|
|
4477
|
-
constructor(deps) {
|
|
4478
|
-
this.deps = deps;
|
|
4479
|
-
this.clustering = deps.clustering ?? new ClusteringService();
|
|
4480
|
-
}
|
|
4481
|
-
deps;
|
|
4482
|
-
clustering;
|
|
4483
|
-
async execute(alerts, correlationId) {
|
|
4484
|
-
const { dedup, traces, llm, notifier, logger: logger8, dedupTtlSeconds } = this.deps;
|
|
4485
|
-
const log = logger8.child({ correlationId, useCase: "ProcessIncident" });
|
|
4486
|
-
log.info({ alertCount: alerts.length }, "Processing alert batch");
|
|
4487
|
-
const clusters = this.clustering.buildClusters(alerts);
|
|
4488
|
-
log.info({ clusterCount: clusters.length }, "Clusters built");
|
|
4489
|
-
this.deps.onClustersBuilt?.(clusters.length);
|
|
4490
|
-
for (const cluster of clusters) {
|
|
4491
|
-
const log2 = log.child({ fingerprint: cluster.fingerprint, service: cluster.serviceName });
|
|
4492
|
-
const isNew = await dedup.isNew(cluster.fingerprint, dedupTtlSeconds);
|
|
4493
|
-
if (!isNew) {
|
|
4494
|
-
log2.debug("Duplicate cluster \u2014 skipping");
|
|
4495
|
-
continue;
|
|
4496
|
-
}
|
|
4497
|
-
const spanLists = await Promise.all(
|
|
4498
|
-
cluster.representativeTraceIds.map(
|
|
4499
|
-
(id) => traces.findByTraceId(id).catch((err) => {
|
|
4500
|
-
log2.warn({ err, traceId: id }, "Trace fetch failed \u2014 continuing without it");
|
|
4501
|
-
return [];
|
|
4502
|
-
})
|
|
4503
|
-
)
|
|
4504
|
-
);
|
|
4505
|
-
const allSpans = spanLists.flat();
|
|
4506
|
-
log2.info({ spanCount: allSpans.length }, "Traces extracted");
|
|
4507
|
-
let analysis = null;
|
|
4508
|
-
try {
|
|
4509
|
-
analysis = await llm.analyze(cluster, allSpans);
|
|
4510
|
-
log2.info({ urgency: analysis.urgency_level }, "LLM analysis complete");
|
|
4511
|
-
} catch (err) {
|
|
4512
|
-
log2.warn({ err }, "LLM inference failed \u2014 notifying without diagnosis");
|
|
4513
|
-
}
|
|
4514
|
-
try {
|
|
4515
|
-
await notifier.send(cluster, analysis);
|
|
4516
|
-
log2.info("Notification sent");
|
|
4517
|
-
} catch (err) {
|
|
4518
|
-
log2.error({ err }, "Notification failed");
|
|
4519
|
-
throw err;
|
|
4520
|
-
}
|
|
4521
|
-
}
|
|
4522
|
-
}
|
|
4523
|
-
};
|
|
4524
|
-
}
|
|
4525
|
-
});
|
|
4526
|
-
|
|
4527
4471
|
// ../../node_modules/.pnpm/prom-client@15.1.3/node_modules/prom-client/lib/util.js
|
|
4528
4472
|
var require_util = __commonJS({
|
|
4529
4473
|
"../../node_modules/.pnpm/prom-client@15.1.3/node_modules/prom-client/lib/util.js"(exports2) {
|
|
@@ -9237,15 +9181,19 @@ __export(metrics_exports, {
|
|
|
9237
9181
|
alertClusters: () => alertClusters,
|
|
9238
9182
|
alertsProcessed: () => alertsProcessed,
|
|
9239
9183
|
alertsReceived: () => alertsReceived,
|
|
9184
|
+
dedupDuplicate: () => dedupDuplicate,
|
|
9185
|
+
dedupNew: () => dedupNew,
|
|
9240
9186
|
dedupRedisFailoverTotal: () => dedupRedisFailoverTotal,
|
|
9241
9187
|
latency: () => latency,
|
|
9242
9188
|
llmInferenceDuration: () => llmInferenceDuration,
|
|
9243
9189
|
llmInferenceTotal: () => llmInferenceTotal,
|
|
9190
|
+
notificationsTotal: () => notificationsTotal,
|
|
9244
9191
|
pipelineInlineFailuresTotal: () => pipelineInlineFailuresTotal,
|
|
9245
9192
|
registry: () => registry,
|
|
9193
|
+
sqsQueueLag: () => sqsQueueLag,
|
|
9246
9194
|
webhookRequestsTotal: () => webhookRequestsTotal
|
|
9247
9195
|
});
|
|
9248
|
-
var import_prom_client, registry, alertsReceived, webhookRequestsTotal, alertsProcessed, alertClusters, latency, llmInferenceDuration, llmInferenceTotal, pipelineInlineFailuresTotal, dedupRedisFailoverTotal;
|
|
9196
|
+
var import_prom_client, registry, alertsReceived, webhookRequestsTotal, alertsProcessed, alertClusters, latency, llmInferenceDuration, llmInferenceTotal, pipelineInlineFailuresTotal, dedupRedisFailoverTotal, dedupNew, dedupDuplicate, notificationsTotal, sqsQueueLag;
|
|
9249
9197
|
var init_metrics2 = __esm({
|
|
9250
9198
|
"../core/src/shared/metrics/index.ts"() {
|
|
9251
9199
|
import_prom_client = __toESM(require_prom_client());
|
|
@@ -9265,6 +9213,7 @@ var init_metrics2 = __esm({
|
|
|
9265
9213
|
alertsProcessed = new import_prom_client.Counter({
|
|
9266
9214
|
name: "junando_alerts_processed_total",
|
|
9267
9215
|
help: "Total number of alerts processed successfully",
|
|
9216
|
+
labelNames: ["result"],
|
|
9268
9217
|
registers: [registry]
|
|
9269
9218
|
});
|
|
9270
9219
|
alertClusters = new import_prom_client.Gauge({
|
|
@@ -9275,7 +9224,8 @@ var init_metrics2 = __esm({
|
|
|
9275
9224
|
latency = new import_prom_client.Histogram({
|
|
9276
9225
|
name: "junando_webhook_duration_seconds",
|
|
9277
9226
|
help: "Webhook request duration in seconds",
|
|
9278
|
-
|
|
9227
|
+
labelNames: ["status"],
|
|
9228
|
+
buckets: [5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5],
|
|
9279
9229
|
registers: [registry]
|
|
9280
9230
|
});
|
|
9281
9231
|
llmInferenceDuration = new import_prom_client.Histogram({
|
|
@@ -9302,6 +9252,89 @@ var init_metrics2 = __esm({
|
|
|
9302
9252
|
help: "Total number of Redis dedup fallback activations",
|
|
9303
9253
|
registers: [registry]
|
|
9304
9254
|
});
|
|
9255
|
+
dedupNew = new import_prom_client.Counter({
|
|
9256
|
+
name: "junando_dedup_new_total",
|
|
9257
|
+
help: "Total number of new alert clusters (not yet seen)",
|
|
9258
|
+
labelNames: ["source"],
|
|
9259
|
+
registers: [registry]
|
|
9260
|
+
});
|
|
9261
|
+
dedupDuplicate = new import_prom_client.Counter({
|
|
9262
|
+
name: "junando_dedup_duplicate_total",
|
|
9263
|
+
help: "Total number of duplicate alert clusters skipped by dedup",
|
|
9264
|
+
labelNames: ["source"],
|
|
9265
|
+
registers: [registry]
|
|
9266
|
+
});
|
|
9267
|
+
notificationsTotal = new import_prom_client.Counter({
|
|
9268
|
+
name: "junando_notifications_total",
|
|
9269
|
+
help: "Total number of notification attempts",
|
|
9270
|
+
labelNames: ["channel", "outcome"],
|
|
9271
|
+
registers: [registry]
|
|
9272
|
+
});
|
|
9273
|
+
sqsQueueLag = new import_prom_client.Gauge({
|
|
9274
|
+
name: "junando_sqs_queue_lag",
|
|
9275
|
+
help: "Approximate number of unprocessed messages in the SQS queue",
|
|
9276
|
+
labelNames: ["queue_name"],
|
|
9277
|
+
registers: [registry]
|
|
9278
|
+
});
|
|
9279
|
+
}
|
|
9280
|
+
});
|
|
9281
|
+
|
|
9282
|
+
// ../core/src/application/use-cases/process-incident.use-case.ts
|
|
9283
|
+
var ProcessIncidentUseCase;
|
|
9284
|
+
var init_process_incident_use_case = __esm({
|
|
9285
|
+
"../core/src/application/use-cases/process-incident.use-case.ts"() {
|
|
9286
|
+
init_clustering_service();
|
|
9287
|
+
init_metrics2();
|
|
9288
|
+
ProcessIncidentUseCase = class {
|
|
9289
|
+
constructor(deps) {
|
|
9290
|
+
this.deps = deps;
|
|
9291
|
+
this.clustering = deps.clustering ?? new ClusteringService();
|
|
9292
|
+
}
|
|
9293
|
+
deps;
|
|
9294
|
+
clustering;
|
|
9295
|
+
async execute(alerts, correlationId) {
|
|
9296
|
+
const { dedup, traces, llm, notifier, logger: logger8, dedupTtlSeconds } = this.deps;
|
|
9297
|
+
const log = logger8.child({ correlationId, useCase: "ProcessIncident" });
|
|
9298
|
+
log.info({ alertCount: alerts.length }, "Processing alert batch");
|
|
9299
|
+
const clusters = this.clustering.buildClusters(alerts);
|
|
9300
|
+
log.info({ clusterCount: clusters.length }, "Clusters built");
|
|
9301
|
+
this.deps.onClustersBuilt?.(clusters.length);
|
|
9302
|
+
for (const cluster of clusters) {
|
|
9303
|
+
const log2 = log.child({ fingerprint: cluster.fingerprint, service: cluster.serviceName });
|
|
9304
|
+
const isNew = await dedup.isNew(cluster.fingerprint, dedupTtlSeconds);
|
|
9305
|
+
if (!isNew) {
|
|
9306
|
+
log2.debug("Duplicate cluster \u2014 skipping");
|
|
9307
|
+
dedupDuplicate.inc({ source: "alertmanager" });
|
|
9308
|
+
continue;
|
|
9309
|
+
}
|
|
9310
|
+
dedupNew.inc({ source: "alertmanager" });
|
|
9311
|
+
const spanLists = await Promise.all(
|
|
9312
|
+
cluster.representativeTraceIds.map(
|
|
9313
|
+
(id) => traces.findByTraceId(id).catch((err) => {
|
|
9314
|
+
log2.warn({ err, traceId: id }, "Trace fetch failed \u2014 continuing without it");
|
|
9315
|
+
return [];
|
|
9316
|
+
})
|
|
9317
|
+
)
|
|
9318
|
+
);
|
|
9319
|
+
const allSpans = spanLists.flat();
|
|
9320
|
+
log2.info({ spanCount: allSpans.length }, "Traces extracted");
|
|
9321
|
+
let analysis = null;
|
|
9322
|
+
try {
|
|
9323
|
+
analysis = await llm.analyze(cluster, allSpans);
|
|
9324
|
+
log2.info({ urgency: analysis.urgency_level }, "LLM analysis complete");
|
|
9325
|
+
} catch (err) {
|
|
9326
|
+
log2.warn({ err }, "LLM inference failed \u2014 notifying without diagnosis");
|
|
9327
|
+
}
|
|
9328
|
+
try {
|
|
9329
|
+
await notifier.send(cluster, analysis);
|
|
9330
|
+
log2.info("Notification sent");
|
|
9331
|
+
} catch (err) {
|
|
9332
|
+
log2.error({ err }, "Notification failed");
|
|
9333
|
+
throw err;
|
|
9334
|
+
}
|
|
9335
|
+
}
|
|
9336
|
+
}
|
|
9337
|
+
};
|
|
9305
9338
|
}
|
|
9306
9339
|
});
|
|
9307
9340
|
|
|
@@ -25820,6 +25853,7 @@ var init_slack_adapter = __esm({
|
|
|
25820
25853
|
"../core/src/infrastructure/notifier/slack.adapter.ts"() {
|
|
25821
25854
|
init_constants();
|
|
25822
25855
|
init_logger();
|
|
25856
|
+
init_metrics2();
|
|
25823
25857
|
logger3 = createLogger();
|
|
25824
25858
|
SlackNotifier = class {
|
|
25825
25859
|
constructor(botToken, channel) {
|
|
@@ -25830,18 +25864,24 @@ var init_slack_adapter = __esm({
|
|
|
25830
25864
|
channel;
|
|
25831
25865
|
async send(cluster, analysis) {
|
|
25832
25866
|
const payload = analysis ? this.buildAnalysisMessage(cluster, analysis) : this.buildFallbackMessage(cluster);
|
|
25833
|
-
|
|
25834
|
-
|
|
25835
|
-
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
25839
|
-
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25867
|
+
try {
|
|
25868
|
+
const res = await fetch(SLACK_API_URL, {
|
|
25869
|
+
method: "POST",
|
|
25870
|
+
headers: {
|
|
25871
|
+
"Content-Type": "application/json",
|
|
25872
|
+
Authorization: `Bearer ${this.botToken}`
|
|
25873
|
+
},
|
|
25874
|
+
body: JSON.stringify({ channel: this.channel, ...payload }),
|
|
25875
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS.Default)
|
|
25876
|
+
});
|
|
25877
|
+
if (!res.ok) throw new Error(`Slack API error: ${res.status}`);
|
|
25878
|
+
const body = await res.json();
|
|
25879
|
+
if (!body.ok) throw new Error(`Slack error: ${body.error}`);
|
|
25880
|
+
notificationsTotal.inc({ channel: "slack", outcome: "success" });
|
|
25881
|
+
} catch (err) {
|
|
25882
|
+
notificationsTotal.inc({ channel: "slack", outcome: "failure" });
|
|
25883
|
+
throw err;
|
|
25884
|
+
}
|
|
25845
25885
|
}
|
|
25846
25886
|
buildAnalysisMessage(cluster, analysis) {
|
|
25847
25887
|
const emoji = URGENCY_EMOJI[analysis.urgency_level] ?? "\u26AA";
|
|
@@ -25945,17 +25985,23 @@ LLM analysis unavailable \u2014 manual investigation required.`
|
|
|
25945
25985
|
ConsoleNotifier = class {
|
|
25946
25986
|
sent = [];
|
|
25947
25987
|
async send(cluster, analysis) {
|
|
25948
|
-
|
|
25949
|
-
|
|
25950
|
-
|
|
25951
|
-
|
|
25952
|
-
|
|
25953
|
-
|
|
25988
|
+
try {
|
|
25989
|
+
this.sent.push({ cluster, analysis });
|
|
25990
|
+
logger3.info(
|
|
25991
|
+
{
|
|
25992
|
+
cluster: {
|
|
25993
|
+
serviceName: cluster.serviceName,
|
|
25994
|
+
alertType: cluster.alertType
|
|
25995
|
+
},
|
|
25996
|
+
analysis: analysis ?? "unavailable"
|
|
25954
25997
|
},
|
|
25955
|
-
|
|
25956
|
-
|
|
25957
|
-
"
|
|
25958
|
-
)
|
|
25998
|
+
"--- Junando Notification ---"
|
|
25999
|
+
);
|
|
26000
|
+
notificationsTotal.inc({ channel: "unknown", outcome: "success" });
|
|
26001
|
+
} catch (err) {
|
|
26002
|
+
notificationsTotal.inc({ channel: "unknown", outcome: "failure" });
|
|
26003
|
+
throw err;
|
|
26004
|
+
}
|
|
25959
26005
|
}
|
|
25960
26006
|
};
|
|
25961
26007
|
}
|
|
@@ -26078,6 +26124,7 @@ var TeamsNotifierError, TeamsNotifier;
|
|
|
26078
26124
|
var init_teams_adapter = __esm({
|
|
26079
26125
|
"../core/src/infrastructure/notifier/teams.adapter.ts"() {
|
|
26080
26126
|
init_constants();
|
|
26127
|
+
init_metrics2();
|
|
26081
26128
|
TeamsNotifierError = class extends Error {
|
|
26082
26129
|
constructor(message) {
|
|
26083
26130
|
super(message);
|
|
@@ -26118,15 +26165,20 @@ var init_teams_adapter = __esm({
|
|
|
26118
26165
|
`Teams webhook error ${res.status} (host: ${this.hostForErrors})`
|
|
26119
26166
|
);
|
|
26120
26167
|
}
|
|
26168
|
+
notificationsTotal.inc({ channel: "teams", outcome: "success" });
|
|
26121
26169
|
} catch (err) {
|
|
26122
26170
|
if (err instanceof TeamsNotifierError) {
|
|
26171
|
+
notificationsTotal.inc({ channel: "teams", outcome: "failure" });
|
|
26123
26172
|
throw err;
|
|
26124
26173
|
}
|
|
26125
26174
|
if (err instanceof Error && err.name === "AbortError") {
|
|
26126
|
-
|
|
26175
|
+
const timeoutErr = new TeamsNotifierError(
|
|
26127
26176
|
`Teams webhook timed out after ${this.timeoutMs}ms (host: ${this.hostForErrors})`
|
|
26128
26177
|
);
|
|
26178
|
+
notificationsTotal.inc({ channel: "teams", outcome: "failure" });
|
|
26179
|
+
throw timeoutErr;
|
|
26129
26180
|
}
|
|
26181
|
+
notificationsTotal.inc({ channel: "teams", outcome: "failure" });
|
|
26130
26182
|
throw err;
|
|
26131
26183
|
} finally {
|
|
26132
26184
|
clearTimeout(timer);
|
|
@@ -53647,6 +53699,36 @@ var init_sqs_adapter = __esm({
|
|
|
53647
53699
|
}
|
|
53648
53700
|
});
|
|
53649
53701
|
|
|
53702
|
+
// ../core/src/infrastructure/queue/sqs-lag-poller.ts
|
|
53703
|
+
function startSqsLagPoller(queueUrl, intervalMs, region) {
|
|
53704
|
+
const client = new SQSClient(region ? { region } : {});
|
|
53705
|
+
const poll = async () => {
|
|
53706
|
+
try {
|
|
53707
|
+
const result = await client.send(
|
|
53708
|
+
new GetQueueAttributesCommand({
|
|
53709
|
+
QueueUrl: queueUrl,
|
|
53710
|
+
AttributeNames: ["ApproximateNumberOfMessages"]
|
|
53711
|
+
})
|
|
53712
|
+
);
|
|
53713
|
+
const raw = result.Attributes?.["ApproximateNumberOfMessages"];
|
|
53714
|
+
if (raw !== void 0) {
|
|
53715
|
+
sqsQueueLag.set({ queue_name: "alerts" }, parseInt(raw, 10));
|
|
53716
|
+
}
|
|
53717
|
+
} catch {
|
|
53718
|
+
}
|
|
53719
|
+
};
|
|
53720
|
+
const timer = setInterval(() => {
|
|
53721
|
+
void poll();
|
|
53722
|
+
}, intervalMs);
|
|
53723
|
+
return () => clearInterval(timer);
|
|
53724
|
+
}
|
|
53725
|
+
var init_sqs_lag_poller = __esm({
|
|
53726
|
+
"../core/src/infrastructure/queue/sqs-lag-poller.ts"() {
|
|
53727
|
+
init_dist_es61();
|
|
53728
|
+
init_metrics2();
|
|
53729
|
+
}
|
|
53730
|
+
});
|
|
53731
|
+
|
|
53650
53732
|
// ../core/src/infrastructure/traces/loki-trace.adapter.ts
|
|
53651
53733
|
var LokiTraceRepository, MockTraceRepository;
|
|
53652
53734
|
var init_loki_trace_adapter = __esm({
|
|
@@ -68746,7 +68828,8 @@ __export(src_exports, {
|
|
|
68746
68828
|
loadConfig: () => loadConfig3,
|
|
68747
68829
|
metrics: () => metrics_exports,
|
|
68748
68830
|
normalizePayload: () => normalizePayload,
|
|
68749
|
-
reinitLogger: () => reinitLogger
|
|
68831
|
+
reinitLogger: () => reinitLogger,
|
|
68832
|
+
startSqsLagPoller: () => startSqsLagPoller
|
|
68750
68833
|
});
|
|
68751
68834
|
var init_src3 = __esm({
|
|
68752
68835
|
"../core/src/index.ts"() {
|
|
@@ -68764,6 +68847,7 @@ var init_src3 = __esm({
|
|
|
68764
68847
|
init_slack_adapter();
|
|
68765
68848
|
init_teams_adapter();
|
|
68766
68849
|
init_sqs_adapter();
|
|
68850
|
+
init_sqs_lag_poller();
|
|
68767
68851
|
init_loki_trace_adapter();
|
|
68768
68852
|
init_config7();
|
|
68769
68853
|
init_constants();
|
|
@@ -68826,6 +68910,7 @@ var handler = async (event) => {
|
|
|
68826
68910
|
};
|
|
68827
68911
|
async function _handler(event) {
|
|
68828
68912
|
const QUEUE_URL = process.env["SQS_QUEUE_URL"] ?? "";
|
|
68913
|
+
const start = Date.now();
|
|
68829
68914
|
const correlationId = crypto2.randomUUID();
|
|
68830
68915
|
if (event.rawPath === "/health") {
|
|
68831
68916
|
metrics_exports.webhookRequestsTotal.inc({ endpoint: "/health", status: "200" });
|
|
@@ -68901,6 +68986,7 @@ async function _handler(event) {
|
|
|
68901
68986
|
const parsed = AlertmanagerPayloadSchema.safeParse(raw);
|
|
68902
68987
|
if (!parsed.success) {
|
|
68903
68988
|
metrics_exports.webhookRequestsTotal.inc({ endpoint: "/webhook/alert", status: "422" });
|
|
68989
|
+
metrics_exports.latency.observe({ status: "error" }, (Date.now() - start) / 1e3);
|
|
68904
68990
|
return {
|
|
68905
68991
|
statusCode: 422,
|
|
68906
68992
|
body: JSON.stringify({
|
|
@@ -68981,6 +69067,7 @@ async function _handler(event) {
|
|
|
68981
69067
|
logger7.error({ err, correlationId }, "Failed to initialize inline pipeline");
|
|
68982
69068
|
}
|
|
68983
69069
|
}
|
|
69070
|
+
metrics_exports.latency.observe({ status: "success" }, (Date.now() - start) / 1e3);
|
|
68984
69071
|
return {
|
|
68985
69072
|
statusCode: 200,
|
|
68986
69073
|
body: JSON.stringify({ accepted: alerts.length, correlationId })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junando/webhook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "AWS Lambda webhook handler — receives and enqueues external alert payloads for Junando",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@aws-sdk/client-sqs": "^3.600.0",
|
|
22
22
|
"zod": "^3.23.0",
|
|
23
|
-
"@junando/core": "0.
|
|
23
|
+
"@junando/core": "0.8.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/aws-lambda": "^8.10.145",
|